From 310c8e40d5cd2512b6d46177563460726cc6166a Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Wed, 27 Jul 2016 21:45:51 -0700 Subject: [PATCH 001/173] mpt3sas: Ensure the connector_name string is NUL-terminated We blindly trust the hardware to give us NUL-terminated strings, which is a bad idea because it doesn't always do that. For example: [ 481.184784] mpt3sas_cm0: enclosure level(0x0000), connector name( \x3) In this case, connector_name is four spaces. We got lucky here because the 2nd byte beyond our character array happens to be a NUL. Fix this by explicitly writing '\0' to the end of the string to ensure we don't run off the edge of the world in printk(). Signed-off-by: Calvin Owens Acked-by: Chaitra P B Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.h | 2 +- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 892c9be008b5..eb7f5b0174ba 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -478,7 +478,7 @@ struct _sas_device { u8 pfa_led_on; u8 pend_sas_rphy_add; u8 enclosure_level; - u8 connector_name[4]; + u8 connector_name[5]; struct kref refcount; }; diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index cd91a684c945..acabe48350fe 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -5380,8 +5380,9 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { sas_device->enclosure_level = le16_to_cpu(sas_device_pg0.EnclosureLevel); - memcpy(&sas_device->connector_name[0], - &sas_device_pg0.ConnectorName[0], 4); + memcpy(sas_device->connector_name, + sas_device_pg0.ConnectorName, 4); + sas_device->connector_name[4] = '\0'; } else { sas_device->enclosure_level = 0; sas_device->connector_name[0] = '\0'; @@ -5508,8 +5509,9 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, if (sas_device_pg0.Flags & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { sas_device->enclosure_level = le16_to_cpu(sas_device_pg0.EnclosureLevel); - memcpy(&sas_device->connector_name[0], - &sas_device_pg0.ConnectorName[0], 4); + memcpy(sas_device->connector_name, + sas_device_pg0.ConnectorName, 4); + sas_device->connector_name[4] = '\0'; } else { sas_device->enclosure_level = 0; sas_device->connector_name[0] = '\0'; From 96902835e7e2c7bf59db28ffd550852610169a2d Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Thu, 28 Jul 2016 21:38:20 -0700 Subject: [PATCH 002/173] mpt3sas: Eliminate conditional locking in mpt3sas_scsih_issue_tm() This flag that conditionally acquires the mutex is confusing and prone to bugginess: refactor it into two separate function calls, and make the unlocked one complain if it's called outside the mutex. Signed-off-by: Calvin Owens Acked-by: Chaitra P B Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.h | 16 +++---- drivers/scsi/mpt3sas/mpt3sas_ctl.c | 5 +-- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 66 +++++++++++++--------------- 3 files changed, 38 insertions(+), 49 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index eb7f5b0174ba..f0baafd9cccc 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -794,16 +794,6 @@ struct reply_post_struct { dma_addr_t reply_post_free_dma; }; -/** - * enum mutex_type - task management mutex type - * @TM_MUTEX_OFF: mutex is not required becuase calling function is acquiring it - * @TM_MUTEX_ON: mutex is required - */ -enum mutex_type { - TM_MUTEX_OFF = 0, - TM_MUTEX_ON = 1, -}; - typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); /** * struct MPT3SAS_ADAPTER - per adapter struct @@ -1291,7 +1281,11 @@ void mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase); int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, uint id, uint lun, u8 type, u16 smid_task, - ulong timeout, enum mutex_type m_type); + ulong timeout); +int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, uint lun, u8 type, u16 smid_task, + ulong timeout); + void mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); void mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); void mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address); diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index 7d00f09666b6..75ae533ac8d9 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -1001,10 +1001,9 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, ioc->name, le16_to_cpu(mpi_request->FunctionDependent1)); mpt3sas_halt_firmware(ioc); - mpt3sas_scsih_issue_tm(ioc, + mpt3sas_scsih_issue_locked_tm(ioc, le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, - 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30, - TM_MUTEX_ON); + 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30); } else mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index acabe48350fe..c93a7ba858ec 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -2201,7 +2201,6 @@ mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task * @timeout: timeout in seconds - * @m_type: TM_MUTEX_ON or TM_MUTEX_OFF * Context: user * * A generic API for sending task management requests to firmware. @@ -2212,8 +2211,7 @@ mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) */ int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, - uint id, uint lun, u8 type, u16 smid_task, ulong timeout, - enum mutex_type m_type) + uint id, uint lun, u8 type, u16 smid_task, ulong timeout) { Mpi2SCSITaskManagementRequest_t *mpi_request; Mpi2SCSITaskManagementReply_t *mpi_reply; @@ -2224,21 +2222,19 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, int rc; u16 msix_task = 0; - if (m_type == TM_MUTEX_ON) - mutex_lock(&ioc->tm_cmds.mutex); + lockdep_assert_held(&ioc->tm_cmds.mutex); + if (ioc->tm_cmds.status != MPT3_CMD_NOT_USED) { pr_info(MPT3SAS_FMT "%s: tm_cmd busy!!!\n", __func__, ioc->name); - rc = FAILED; - goto err_out; + return FAILED; } if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery) { pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", __func__, ioc->name); - rc = FAILED; - goto err_out; + return FAILED; } ioc_state = mpt3sas_base_get_iocstate(ioc, 0); @@ -2247,8 +2243,7 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, "unexpected doorbell active!\n", ioc->name)); rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); - rc = (!rc) ? SUCCESS : FAILED; - goto err_out; + return (!rc) ? SUCCESS : FAILED; } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { @@ -2256,16 +2251,14 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, MPI2_DOORBELL_DATA_MASK); rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); - rc = (!rc) ? SUCCESS : FAILED; - goto err_out; + return (!rc) ? SUCCESS : FAILED; } smid = mpt3sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); if (!smid) { pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - rc = FAILED; - goto err_out; + return FAILED; } if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) @@ -2302,9 +2295,7 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); rc = (!rc) ? SUCCESS : FAILED; - ioc->tm_cmds.status = MPT3_CMD_NOT_USED; - mpt3sas_scsih_clear_tm_flag(ioc, handle); - goto err_out; + goto out; } } @@ -2356,17 +2347,23 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, break; } +out: mpt3sas_scsih_clear_tm_flag(ioc, handle); ioc->tm_cmds.status = MPT3_CMD_NOT_USED; - if (m_type == TM_MUTEX_ON) - mutex_unlock(&ioc->tm_cmds.mutex); - return rc; +} - err_out: - if (m_type == TM_MUTEX_ON) - mutex_unlock(&ioc->tm_cmds.mutex); - return rc; +int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, uint lun, u8 type, u16 smid_task, ulong timeout) +{ + int ret; + + mutex_lock(&ioc->tm_cmds.mutex); + ret = mpt3sas_scsih_issue_tm(ioc, handle, channel, id, lun, type, + smid_task, timeout); + mutex_unlock(&ioc->tm_cmds.mutex); + + return ret; } /** @@ -2482,9 +2479,9 @@ scsih_abort(struct scsi_cmnd *scmd) mpt3sas_halt_firmware(ioc); handle = sas_device_priv_data->sas_target->handle; - r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, scmd->device->id, scmd->device->lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, TM_MUTEX_ON); + MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30); out: sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n", @@ -2541,9 +2538,9 @@ scsih_dev_reset(struct scsi_cmnd *scmd) goto out; } - r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, scmd->device->id, scmd->device->lun, - MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, TM_MUTEX_ON); + MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30); out: sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", @@ -2603,9 +2600,9 @@ scsih_target_reset(struct scsi_cmnd *scmd) goto out; } - r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, - 30, TM_MUTEX_ON); + 30); out: starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", @@ -6089,8 +6086,7 @@ _scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); r = mpt3sas_scsih_issue_tm(ioc, handle, 0, 0, lun, - MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, - TM_MUTEX_OFF); + MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30); if (r == FAILED) { sdev_printk(KERN_WARNING, sdev, "mpt3sas_scsih_issue_tm: FAILED when sending " @@ -6130,8 +6126,8 @@ _scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, goto out_no_lock; r = mpt3sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, - sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, - TM_MUTEX_OFF); + sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, + 30); if (r == FAILED) { sdev_printk(KERN_WARNING, sdev, "mpt3sas_scsih_issue_tm: ABORT_TASK: FAILED : " From 98c56ad32c33f0fd6742502bafe7003e4b8c80ed Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Thu, 28 Jul 2016 21:38:21 -0700 Subject: [PATCH 003/173] mpt3sas: Eliminate dead sleep_flag code With the exception of a single call to wait_for_doorbell_int(), all this conditional sleeping code is dead. So delete it. Signed-off-by: Calvin Owens Acked-by: Chaitra P B Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 241 ++++++++++------------- drivers/scsi/mpt3sas/mpt3sas_base.h | 6 +- drivers/scsi/mpt3sas/mpt3sas_config.c | 3 +- drivers/scsi/mpt3sas/mpt3sas_ctl.c | 15 +- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 21 +- drivers/scsi/mpt3sas/mpt3sas_transport.c | 12 +- 6 files changed, 120 insertions(+), 178 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 751f13edece0..095618328820 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -98,7 +98,7 @@ MODULE_PARM_DESC(mpt3sas_fwfault_debug, " enable detection of firmware fault and halt firmware - (default=0)"); static int -_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); +_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc); /** * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. @@ -218,8 +218,7 @@ _base_fault_reset_work(struct work_struct *work) ioc->non_operational_loop = 0; if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) { - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); pr_warn(MPT3SAS_FMT "%s: hard reset: %s\n", ioc->name, __func__, (rc == 0) ? "success" : "failed"); doorbell = mpt3sas_base_get_iocstate(ioc, 0); @@ -2145,7 +2144,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) _base_mask_interrupts(ioc); - r = _base_get_ioc_facts(ioc, CAN_SLEEP); + r = _base_get_ioc_facts(ioc); if (r) goto out_fail; @@ -3172,12 +3171,11 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) /** * _base_allocate_memory_pools - allocate start of day memory pools * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 success, anything else error */ static int -_base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) { struct mpt3sas_facts *facts; u16 max_sge_elements; @@ -3647,29 +3645,25 @@ mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked) * _base_wait_on_iocstate - waiting on a particular ioc state * @ioc_state: controller state { READY, OPERATIONAL, or RESET } * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout, - int sleep_flag) +_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout) { u32 count, cntdn; u32 current_state; count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + cntdn = 1000 * timeout; do { current_state = mpt3sas_base_get_iocstate(ioc, 1); if (current_state == ioc_state) return 0; if (count && current_state == MPI2_IOC_STATE_FAULT) break; - if (sleep_flag == CAN_SLEEP) - usleep_range(1000, 1500); - else - udelay(500); + + usleep_range(1000, 1500); count++; } while (--cntdn); @@ -3681,24 +3675,22 @@ _base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout, * a write to the doorbell) * @ioc: per adapter object * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. * * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. */ static int -_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); +_base_diag_reset(struct MPT3SAS_ADAPTER *ioc); static int -_base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, - int sleep_flag) +_base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout) { u32 cntdn, count; u32 int_status; count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + cntdn = 1000 * timeout; do { int_status = readl(&ioc->chip->HostInterruptStatus); if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { @@ -3707,10 +3699,8 @@ _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, ioc->name, __func__, count, timeout)); return 0; } - if (sleep_flag == CAN_SLEEP) - usleep_range(1000, 1500); - else - udelay(500); + + usleep_range(1000, 1500); count++; } while (--cntdn); @@ -3720,11 +3710,38 @@ _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, return -EFAULT; } +static int +_base_spin_on_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout) +{ + u32 cntdn, count; + u32 int_status; + + count = 0; + cntdn = 2000 * timeout; + do { + int_status = readl(&ioc->chip->HostInterruptStatus); + if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { + dhsprintk(ioc, pr_info(MPT3SAS_FMT + "%s: successful count(%d), timeout(%d)\n", + ioc->name, __func__, count, timeout)); + return 0; + } + + udelay(500); + count++; + } while (--cntdn); + + pr_err(MPT3SAS_FMT + "%s: failed due to timeout count(%d), int_status(%x)!\n", + ioc->name, __func__, count, int_status); + return -EFAULT; + +} + /** * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell. * @ioc: per adapter object * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. * @@ -3732,15 +3749,14 @@ _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, * doorbell. */ static int -_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout, - int sleep_flag) +_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout) { u32 cntdn, count; u32 int_status; u32 doorbell; count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + cntdn = 1000 * timeout; do { int_status = readl(&ioc->chip->HostInterruptStatus); if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) { @@ -3758,10 +3774,7 @@ _base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout, } else if (int_status == 0xFFFFFFFF) goto out; - if (sleep_flag == CAN_SLEEP) - usleep_range(1000, 1500); - else - udelay(500); + usleep_range(1000, 1500); count++; } while (--cntdn); @@ -3776,20 +3789,18 @@ _base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout, * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use * @ioc: per adapter object * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. * */ static int -_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout, - int sleep_flag) +_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout) { u32 cntdn, count; u32 doorbell_reg; count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + cntdn = 1000 * timeout; do { doorbell_reg = readl(&ioc->chip->Doorbell); if (!(doorbell_reg & MPI2_DOORBELL_USED)) { @@ -3798,10 +3809,8 @@ _base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout, ioc->name, __func__, count, timeout)); return 0; } - if (sleep_flag == CAN_SLEEP) - usleep_range(1000, 1500); - else - udelay(500); + + usleep_range(1000, 1500); count++; } while (--cntdn); @@ -3816,13 +3825,11 @@ _base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout, * @ioc: per adapter object * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout, - int sleep_flag) +_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout) { u32 ioc_state; int r = 0; @@ -3841,12 +3848,11 @@ _base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout, writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT, &ioc->chip->Doorbell); - if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) { + if ((_base_wait_for_doorbell_ack(ioc, 15))) { r = -EFAULT; goto out; } - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, - timeout, sleep_flag); + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout); if (ioc_state) { pr_err(MPT3SAS_FMT "%s: failed going to ready state (ioc_state=0x%x)\n", @@ -3868,13 +3874,12 @@ _base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout, * @reply_bytes: reply length * @reply: pointer to reply payload * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, - u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag) + u32 *request, int reply_bytes, u16 *reply, int timeout) { MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; int i; @@ -3900,7 +3905,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, ((request_bytes/4)<chip->Doorbell); - if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) { + if ((_base_spin_on_doorbell_int(ioc, 5))) { pr_err(MPT3SAS_FMT "doorbell handshake int failed (line=%d)\n", ioc->name, __LINE__); @@ -3908,7 +3913,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, } writel(0, &ioc->chip->HostInterruptStatus); - if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) { + if ((_base_wait_for_doorbell_ack(ioc, 5))) { pr_err(MPT3SAS_FMT "doorbell handshake ack failed (line=%d)\n", ioc->name, __LINE__); @@ -3918,7 +3923,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, /* send message 32-bits at a time */ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) { writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell); - if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) + if ((_base_wait_for_doorbell_ack(ioc, 5))) failed = 1; } @@ -3930,7 +3935,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, } /* now wait for the reply */ - if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) { + if ((_base_wait_for_doorbell_int(ioc, timeout))) { pr_err(MPT3SAS_FMT "doorbell handshake int failed (line=%d)\n", ioc->name, __LINE__); @@ -3941,7 +3946,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); - if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + if ((_base_wait_for_doorbell_int(ioc, 5))) { pr_err(MPT3SAS_FMT "doorbell handshake int failed (line=%d)\n", ioc->name, __LINE__); @@ -3952,7 +3957,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, writel(0, &ioc->chip->HostInterruptStatus); for (i = 2; i < default_reply->MsgLength * 2; i++) { - if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + if ((_base_wait_for_doorbell_int(ioc, 5))) { pr_err(MPT3SAS_FMT "doorbell handshake int failed (line=%d)\n", ioc->name, __LINE__); @@ -3966,8 +3971,8 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, writel(0, &ioc->chip->HostInterruptStatus); } - _base_wait_for_doorbell_int(ioc, 5, sleep_flag); - if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) { + _base_wait_for_doorbell_int(ioc, 5); + if (_base_wait_for_doorbell_not_used(ioc, 5) != 0) { dhsprintk(ioc, pr_info(MPT3SAS_FMT "doorbell is in use (line=%d)\n", ioc->name, __LINE__)); } @@ -4082,8 +4087,7 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); ioc->base_cmds.status = MPT3_CMD_NOT_USED; rc = -EFAULT; out: @@ -4180,8 +4184,7 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); ioc->base_cmds.status = MPT3_CMD_NOT_USED; rc = -EFAULT; out: @@ -4192,12 +4195,11 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, /** * _base_get_port_facts - obtain port facts reply and save in ioc * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) +_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port) { Mpi2PortFactsRequest_t mpi_request; Mpi2PortFactsReply_t mpi_reply; @@ -4213,7 +4215,7 @@ _base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) mpi_request.Function = MPI2_FUNCTION_PORT_FACTS; mpi_request.PortNumber = port; r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, - (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); + (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5); if (r != 0) { pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", @@ -4236,13 +4238,11 @@ _base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL * @ioc: per adapter object * @timeout: - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, - int sleep_flag) +_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout) { u32 ioc_state; int rc; @@ -4276,8 +4276,7 @@ _base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, goto issue_diag_reset; } - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, - timeout, sleep_flag); + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout); if (ioc_state) { dfailprintk(ioc, printk(MPT3SAS_FMT "%s: failed going to ready state (ioc_state=0x%x)\n", @@ -4286,19 +4285,18 @@ _base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, } issue_diag_reset: - rc = _base_diag_reset(ioc, sleep_flag); + rc = _base_diag_reset(ioc); return rc; } /** * _base_get_ioc_facts - obtain ioc facts reply and save in ioc * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc) { Mpi2IOCFactsRequest_t mpi_request; Mpi2IOCFactsReply_t mpi_reply; @@ -4308,7 +4306,7 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); - r = _base_wait_for_iocstate(ioc, 10, sleep_flag); + r = _base_wait_for_iocstate(ioc, 10); if (r) { dfailprintk(ioc, printk(MPT3SAS_FMT "%s: failed getting to correct state\n", @@ -4320,7 +4318,7 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) memset(&mpi_request, 0, mpi_request_sz); mpi_request.Function = MPI2_FUNCTION_IOC_FACTS; r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, - (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); + (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5); if (r != 0) { pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", @@ -4380,12 +4378,11 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /** * _base_send_ioc_init - send ioc_init to firmware * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) { Mpi2IOCInitRequest_t mpi_request; Mpi2IOCInitReply_t mpi_reply; @@ -4468,8 +4465,7 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) r = _base_handshake_req_reply_wait(ioc, sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, - sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10, - sleep_flag); + sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10); if (r != 0) { pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", @@ -4544,12 +4540,11 @@ mpt3sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, /** * _base_send_port_enable - send port_enable(discovery stuff) to firmware * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_port_enable(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_send_port_enable(struct MPT3SAS_ADAPTER *ioc) { Mpi2PortEnableRequest_t *mpi_request; Mpi2PortEnableReply_t *mpi_reply; @@ -4726,12 +4721,11 @@ _base_unmask_events(struct MPT3SAS_ADAPTER *ioc, u16 event) /** * _base_event_notification - send event notification * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_event_notification(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_event_notification(struct MPT3SAS_ADAPTER *ioc) { Mpi2EventNotificationRequest_t *mpi_request; unsigned long timeleft; @@ -4816,19 +4810,18 @@ mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type) return; mutex_lock(&ioc->base_cmds.mutex); - _base_event_notification(ioc, CAN_SLEEP); + _base_event_notification(ioc); mutex_unlock(&ioc->base_cmds.mutex); } /** * _base_diag_reset - the "big hammer" start of day reset * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_diag_reset(struct MPT3SAS_ADAPTER *ioc) { u32 host_diagnostic; u32 ioc_state; @@ -4856,10 +4849,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence); /* wait 100 msec */ - if (sleep_flag == CAN_SLEEP) - msleep(100); - else - mdelay(100); + msleep(100); if (count++ > 20) goto out; @@ -4879,10 +4869,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) &ioc->chip->HostDiagnostic); /*This delay allows the chip PCIe hardware time to finish reset tasks*/ - if (sleep_flag == CAN_SLEEP) - msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); - else - mdelay(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); + msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); /* Approximately 300 second max wait */ for (count = 0; count < (300000000 / @@ -4895,13 +4882,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER)) break; - /* Wait to pass the second read delay window */ - if (sleep_flag == CAN_SLEEP) - msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC - / 1000); - else - mdelay(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC - / 1000); + msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC / 1000); } if (host_diagnostic & MPI2_DIAG_HCB_MODE) { @@ -4930,8 +4911,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) drsprintk(ioc, pr_info(MPT3SAS_FMT "Wait for FW to go to the READY state\n", ioc->name)); - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20, - sleep_flag); + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20); if (ioc_state) { pr_err(MPT3SAS_FMT "%s: failed going to ready state (ioc_state=0x%x)\n", @@ -4950,14 +4930,12 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /** * _base_make_ioc_ready - put controller in READY state * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * @type: FORCE_BIG_HAMMER or SOFT_RESET * * Returns 0 for success, non-zero for failure. */ static int -_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, - enum reset_type type) +_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) { u32 ioc_state; int rc; @@ -4984,10 +4962,7 @@ _base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, ioc->name, __func__, ioc_state); return -EFAULT; } - if (sleep_flag == CAN_SLEEP) - ssleep(1); - else - mdelay(1000); + ssleep(1); ioc_state = mpt3sas_base_get_iocstate(ioc, 0); } } @@ -5013,24 +4988,23 @@ _base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) if (!(_base_send_ioc_reset(ioc, - MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) { + MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15))) { return 0; } issue_diag_reset: - rc = _base_diag_reset(ioc, CAN_SLEEP); + rc = _base_diag_reset(ioc); return rc; } /** * _base_make_ioc_operational - put controller in OPERATIONAL state * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) { int r, i, index; unsigned long flags; @@ -5149,7 +5123,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) } skip_init_reply_post_free_queue: - r = _base_send_ioc_init(ioc, sleep_flag); + r = _base_send_ioc_init(ioc); if (r) return r; @@ -5175,13 +5149,11 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) skip_init_reply_post_host_index: _base_unmask_interrupts(ioc); - r = _base_event_notification(ioc, sleep_flag); + r = _base_event_notification(ioc); if (r) return r; - if (sleep_flag == CAN_SLEEP) - _base_static_config_pages(ioc); - + _base_static_config_pages(ioc); if (ioc->is_driver_loading) { @@ -5200,7 +5172,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) return r; /* scan_start and scan_finished support */ } - r = _base_send_port_enable(ioc, sleep_flag); + r = _base_send_port_enable(ioc); if (r) return r; @@ -5224,7 +5196,7 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) if (ioc->chip_phys && ioc->chip) { _base_mask_interrupts(ioc); ioc->shost_recovery = 1; - _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + _base_make_ioc_ready(ioc, SOFT_RESET); ioc->shost_recovery = 0; } @@ -5292,7 +5264,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) } pci_set_drvdata(ioc->pdev, ioc->shost); - r = _base_get_ioc_facts(ioc, CAN_SLEEP); + r = _base_get_ioc_facts(ioc); if (r) goto out_free_resources; @@ -5326,7 +5298,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->build_sg_mpi = &_base_build_sg; ioc->build_zero_len_sge_mpi = &_base_build_zero_len_sge; - r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + r = _base_make_ioc_ready(ioc, SOFT_RESET); if (r) goto out_free_resources; @@ -5338,12 +5310,12 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) } for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { - r = _base_get_port_facts(ioc, i, CAN_SLEEP); + r = _base_get_port_facts(ioc, i); if (r) goto out_free_resources; } - r = _base_allocate_memory_pools(ioc, CAN_SLEEP); + r = _base_allocate_memory_pools(ioc); if (r) goto out_free_resources; @@ -5429,7 +5401,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) if (ioc->hba_mpi_version_belonged == MPI26_VERSION) _base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); - r = _base_make_ioc_operational(ioc, CAN_SLEEP); + r = _base_make_ioc_operational(ioc); if (r) goto out_free_resources; @@ -5565,21 +5537,18 @@ _base_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) /** * _wait_for_commands_to_complete - reset controller * @ioc: Pointer to MPT_ADAPTER structure - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * This function waiting(3s) for all pending commands to complete * prior to putting controller in reset. */ static void -_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc) { u32 ioc_state; unsigned long flags; u16 i; ioc->pending_io_count = 0; - if (sleep_flag != CAN_SLEEP) - return; ioc_state = mpt3sas_base_get_iocstate(ioc, 0); if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) @@ -5602,13 +5571,12 @@ _wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /** * mpt3sas_base_hard_reset_handler - reset controller * @ioc: Pointer to MPT_ADAPTER structure - * @sleep_flag: CAN_SLEEP or NO_SLEEP * @type: FORCE_BIG_HAMMER or SOFT_RESET * * Returns 0 for success, non-zero for failure. */ int -mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, +mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) { int r; @@ -5629,13 +5597,6 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, if (mpt3sas_fwfault_debug) mpt3sas_halt_firmware(ioc); - /* TODO - What we really should be doing is pulling - * out all the code associated with NO_SLEEP; its never used. - * That is legacy code from mpt fusion driver, ported over. - * I will leave this BUG_ON here for now till its been resolved. - */ - BUG_ON(sleep_flag == NO_SLEEP); - /* wait for an active reset in progress to complete */ if (!mutex_trylock(&ioc->reset_in_progress_mutex)) { do { @@ -5660,9 +5621,9 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, is_fault = 1; } _base_reset_handler(ioc, MPT3_IOC_PRE_RESET); - _wait_for_commands_to_complete(ioc, sleep_flag); + _wait_for_commands_to_complete(ioc); _base_mask_interrupts(ioc); - r = _base_make_ioc_ready(ioc, sleep_flag, type); + r = _base_make_ioc_ready(ioc, type); if (r) goto out; _base_reset_handler(ioc, MPT3_IOC_AFTER_RESET); @@ -5675,7 +5636,7 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, r = -EFAULT; goto out; } - r = _base_get_ioc_facts(ioc, CAN_SLEEP); + r = _base_get_ioc_facts(ioc); if (r) goto out; @@ -5684,7 +5645,7 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, "Please reboot the system and ensure that the correct" " firmware version is running\n", ioc->name); - r = _base_make_ioc_operational(ioc, sleep_flag); + r = _base_make_ioc_operational(ioc); if (!r) _base_reset_handler(ioc, MPT3_IOC_DONE_RESET); diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index f0baafd9cccc..3e71bc1b4a80 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -119,10 +119,6 @@ #define MPT_MAX_CALLBACKS 32 - -#define CAN_SLEEP 1 -#define NO_SLEEP 0 - #define INTERNAL_CMDS_COUNT 10 /* reserved cmds */ /* reserved for issuing internally framed scsi io cmds */ #define INTERNAL_SCSIIO_CMDS_COUNT 3 @@ -1219,7 +1215,7 @@ int mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc); int mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc); -int mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, +int mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, enum reset_type type); void *mpt3sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid); diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c index a6914ec99cc0..022f27f3ef6e 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_config.c +++ b/drivers/scsi/mpt3sas/mpt3sas_config.c @@ -491,8 +491,7 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t mutex_unlock(&ioc->config_cmds.mutex); if (issue_host_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return r; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index 75ae533ac8d9..c924012d62d1 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -1005,8 +1005,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30); } else - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); } out: @@ -1219,8 +1218,7 @@ _ctl_do_reset(struct MPT3SAS_ADAPTER *ioc, void __user *arg) dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, __func__)); - retval = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); pr_info(MPT3SAS_FMT "host reset: %s\n", ioc->name, ((!retval) ? "SUCCESS" : "FAILED")); return 0; @@ -1537,8 +1535,7 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: @@ -1973,8 +1970,7 @@ _ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg) rc = mpt3sas_send_diag_release(ioc, buffer_type, &issue_reset); if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return rc; } @@ -2154,8 +2150,7 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index c93a7ba858ec..22d75a2a6672 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -2241,16 +2241,14 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, if (ioc_state & MPI2_DOORBELL_USED) { dhsprintk(ioc, pr_info(MPT3SAS_FMT "unexpected doorbell active!\n", ioc->name)); - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return (!rc) ? SUCCESS : FAILED; } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { mpt3sas_base_fault_info(ioc, ioc_state & MPI2_DOORBELL_DATA_MASK); - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return (!rc) ? SUCCESS : FAILED; } @@ -2292,8 +2290,8 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, _debug_dump_mf(mpi_request, sizeof(Mpi2SCSITaskManagementRequest_t)/4); if (!(ioc->tm_cmds.status & MPT3_CMD_RESET)) { - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + rc = mpt3sas_base_hard_reset_handler(ioc, + FORCE_BIG_HAMMER); rc = (!rc) ? SUCCESS : FAILED; goto out; } @@ -2638,8 +2636,7 @@ scsih_host_reset(struct scsi_cmnd *scmd) goto out; } - retval = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); r = (retval < 0) ? FAILED : SUCCESS; out: pr_info(MPT3SAS_FMT "host reset: %s scmd(%p)\n", @@ -6295,8 +6292,7 @@ _scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num) mutex_unlock(&ioc->scsih_cmds.mutex); if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return rc; } @@ -8914,7 +8910,7 @@ scsih_resume(struct pci_dev *pdev) if (r) return r; - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET); + mpt3sas_base_hard_reset_handler(ioc, SOFT_RESET); scsi_unblock_requests(shost); mpt3sas_base_start_watchdog(ioc); return 0; @@ -8985,8 +8981,7 @@ scsih_pci_slot_reset(struct pci_dev *pdev) if (rc) return PCI_ERS_RESULT_DISCONNECT; - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); pr_warn(MPT3SAS_FMT "hard reset: %s\n", ioc->name, (rc == 0) ? "success" : "failed"); diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c index ff93286bc32f..f58a8c61fdec 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_transport.c +++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c @@ -446,8 +446,7 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: ioc->transport_cmds.status = MPT3_CMD_NOT_USED; if (data_out) @@ -1253,8 +1252,7 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: ioc->transport_cmds.status = MPT3_CMD_NOT_USED; if (data_out) @@ -1564,8 +1562,7 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: ioc->transport_cmds.status = MPT3_CMD_NOT_USED; if (data_out) @@ -2103,8 +2100,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, issue_host_reset: if (issue_reset) { - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); rc = -ETIMEDOUT; } From 8bbb1cf63f5e345685d42749606d0474cfde6def Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Thu, 28 Jul 2016 21:38:22 -0700 Subject: [PATCH 004/173] mpt3sas: Fix warnings exposed by W=1 Trivial non-functional changes for a couple annoying things: 1) Functions local to files are not declared static, which is frustrating when reading the code because it's non-obvious at first glance what's actually called from other files. 2) Set-but-unused variables abound, presumably to mask -Wunused-result errors in the past. None of these are flagged today though (with one exception noted below), so remove them. Fixing (2) exposed the fact that we improperly ignore the return value of scsi_device_reprobe() in _scsih_reprobe_lun(). Fixing the calling code to deal with the potential error is non-trivial, so for now just WARN(). Signed-off-by: Calvin Owens Acked-by: Chaitra P B Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 18 ++---- drivers/scsi/mpt3sas/mpt3sas_config.c | 4 +- drivers/scsi/mpt3sas/mpt3sas_ctl.c | 29 ++++------ drivers/scsi/mpt3sas/mpt3sas_scsih.c | 70 +++++++++++------------- drivers/scsi/mpt3sas/mpt3sas_transport.c | 16 ++---- 5 files changed, 56 insertions(+), 81 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 095618328820..df95d1aa1eaa 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -2039,7 +2039,7 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) * mpt3sas_base_unmap_resources - free controller resources * @ioc: per adapter object */ -void +static void mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc) { struct pci_dev *pdev = ioc->pdev; @@ -3884,7 +3884,6 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; int i; u8 failed; - u16 dummy; __le32 *mfp; /* make sure doorbell is not in use */ @@ -3964,7 +3963,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, return -EFAULT; } if (i >= reply_bytes/2) /* overflow case */ - dummy = readl(&ioc->chip->Doorbell); + readl(&ioc->chip->Doorbell); else reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); @@ -4009,7 +4008,6 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, { u16 smid; u32 ioc_state; - unsigned long timeleft; bool issue_reset = false; int rc; void *request; @@ -4062,7 +4060,7 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, ioc->ioc_link_reset_in_progress = 1; init_completion(&ioc->base_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, + wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) && @@ -4112,7 +4110,6 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, { u16 smid; u32 ioc_state; - unsigned long timeleft; bool issue_reset = false; int rc; void *request; @@ -4163,7 +4160,7 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); init_completion(&ioc->base_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, + wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", @@ -4548,7 +4545,6 @@ _base_send_port_enable(struct MPT3SAS_ADAPTER *ioc) { Mpi2PortEnableRequest_t *mpi_request; Mpi2PortEnableReply_t *mpi_reply; - unsigned long timeleft; int r = 0; u16 smid; u16 ioc_status; @@ -4576,8 +4572,7 @@ _base_send_port_enable(struct MPT3SAS_ADAPTER *ioc) init_completion(&ioc->port_enable_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done, - 300*HZ); + wait_for_completion_timeout(&ioc->port_enable_cmds.done, 300*HZ); if (!(ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, __func__); @@ -4728,7 +4723,6 @@ static int _base_event_notification(struct MPT3SAS_ADAPTER *ioc) { Mpi2EventNotificationRequest_t *mpi_request; - unsigned long timeleft; u16 smid; int r = 0; int i; @@ -4760,7 +4754,7 @@ _base_event_notification(struct MPT3SAS_ADAPTER *ioc) cpu_to_le32(ioc->event_masks[i]); init_completion(&ioc->base_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); + wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, __func__); diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c index 022f27f3ef6e..cebfd734fd76 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_config.c +++ b/drivers/scsi/mpt3sas/mpt3sas_config.c @@ -285,7 +285,6 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t { u16 smid; u32 ioc_state; - unsigned long timeleft; Mpi2ConfigRequest_t *config_request; int r; u8 retry_count, issue_host_reset = 0; @@ -386,8 +385,7 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t _config_display_some_debug(ioc, smid, "config_request", NULL); init_completion(&ioc->config_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, - timeout*HZ); + wait_for_completion_timeout(&ioc->config_cmds.done, timeout*HZ); if (!(ioc->config_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, __func__); diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index c924012d62d1..26cdc127ac89 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -518,7 +518,7 @@ mpt3sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) * * Called when application request fasyn callback handler. */ -int +static int _ctl_fasync(int fd, struct file *filep, int mode) { return fasync_helper(fd, filep, mode, &async_queue); @@ -530,7 +530,7 @@ _ctl_fasync(int fd, struct file *filep, int mode) * @wait - * */ -unsigned int +static unsigned int _ctl_poll(struct file *filep, poll_table *wait) { struct MPT3SAS_ADAPTER *ioc; @@ -641,9 +641,8 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, MPI2RequestHeader_t *mpi_request = NULL, *request; MPI2DefaultReply_t *mpi_reply; u32 ioc_state; - u16 ioc_status; u16 smid; - unsigned long timeout, timeleft; + unsigned long timeout; u8 issue_reset; u32 sz; void *psge; @@ -914,8 +913,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, timeout = MPT3_IOCTL_DEFAULT_TIMEOUT; else timeout = karg.timeout; - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, - timeout*HZ); + wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)mpi_request; @@ -938,7 +936,6 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, } mpi_reply = ioc->ctl_cmds.reply; - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT && (ioc->logging_level & MPT_DEBUG_TM)) { @@ -1378,7 +1375,6 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, Mpi2DiagBufferPostRequest_t *mpi_request; Mpi2DiagBufferPostReply_t *mpi_reply; u8 buffer_type; - unsigned long timeleft; u16 smid; u16 ioc_status; u32 ioc_state; @@ -1496,7 +1492,7 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, init_completion(&ioc->ctl_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { @@ -1796,7 +1792,6 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, u16 ioc_status; u32 ioc_state; int rc; - unsigned long timeleft; dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); @@ -1844,7 +1839,7 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, init_completion(&ioc->ctl_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { @@ -1990,7 +1985,7 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) Mpi2DiagBufferPostReply_t *mpi_reply; int rc, i; u8 buffer_type; - unsigned long timeleft, request_size, copy_size; + unsigned long request_size, copy_size; u16 smid; u16 ioc_status; u8 issue_reset = 0; @@ -2111,7 +2106,7 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) init_completion(&ioc->ctl_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { @@ -2346,7 +2341,7 @@ out_unlock_pciaccess: * @cmd - ioctl opcode * @arg - */ -long +static long _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; @@ -2366,7 +2361,7 @@ _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * @cmd - ioctl opcode * @arg - */ -long +static long _ctl_mpt2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; @@ -2386,7 +2381,7 @@ _ctl_mpt2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * * This routine handles 32 bit applications in 64bit os. */ -long +static long _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; @@ -2404,7 +2399,7 @@ _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) * * This routine handles 32 bit applications in 64bit os. */ -long +static long _ctl_mpt2_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 22d75a2a6672..7732e3ef2db7 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -1195,7 +1195,7 @@ _scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id, * * Returns queue depth. */ -int +static int scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) { struct Scsi_Host *shost = sdev->host; @@ -1244,7 +1244,7 @@ scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -int +static int scsih_target_alloc(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(&starget->dev); @@ -1311,7 +1311,7 @@ scsih_target_alloc(struct scsi_target *starget) * * Returns nothing. */ -void +static void scsih_target_destroy(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(&starget->dev); @@ -1320,7 +1320,6 @@ scsih_target_destroy(struct scsi_target *starget) struct _sas_device *sas_device; struct _raid_device *raid_device; unsigned long flags; - struct sas_rphy *rphy; sas_target_priv_data = starget->hostdata; if (!sas_target_priv_data) @@ -1339,7 +1338,6 @@ scsih_target_destroy(struct scsi_target *starget) } spin_lock_irqsave(&ioc->sas_device_lock, flags); - rphy = dev_to_rphy(starget->dev.parent); sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data); if (sas_device && (sas_device->starget == starget) && (sas_device->id == starget->id) && @@ -1369,7 +1367,7 @@ scsih_target_destroy(struct scsi_target *starget) * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -int +static int scsih_slave_alloc(struct scsi_device *sdev) { struct Scsi_Host *shost; @@ -1434,7 +1432,7 @@ scsih_slave_alloc(struct scsi_device *sdev) * * Returns nothing. */ -void +static void scsih_slave_destroy(struct scsi_device *sdev) { struct MPT3SAS_TARGET *sas_target_priv_data; @@ -1527,7 +1525,7 @@ _scsih_display_sata_capabilities(struct MPT3SAS_ADAPTER *ioc, * scsih_is_raid - return boolean indicating device is raid volume * @dev the device struct object */ -int +static int scsih_is_raid(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); @@ -1542,7 +1540,7 @@ scsih_is_raid(struct device *dev) * scsih_get_resync - get raid volume resync percent complete * @dev the device struct object */ -void +static void scsih_get_resync(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); @@ -1603,7 +1601,7 @@ scsih_get_resync(struct device *dev) * scsih_get_state - get raid volume level * @dev the device struct object */ -void +static void scsih_get_state(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); @@ -1805,7 +1803,7 @@ _scsih_enable_tlr(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev) * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -int +static int scsih_slave_configure(struct scsi_device *sdev) { struct Scsi_Host *shost = sdev->host; @@ -2021,7 +2019,7 @@ scsih_slave_configure(struct scsi_device *sdev) * * Return nothing. */ -int +static int scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int params[]) { @@ -2217,7 +2215,6 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, Mpi2SCSITaskManagementReply_t *mpi_reply; u16 smid = 0; u32 ioc_state; - unsigned long timeleft; struct scsiio_tracker *scsi_lookup = NULL; int rc; u16 msix_task = 0; @@ -2283,7 +2280,7 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, else msix_task = 0; mpt3sas_base_put_smid_hi_priority(ioc, smid, msix_task); - timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); + wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); if (!(ioc->tm_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, __func__); @@ -2434,7 +2431,7 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) * * Returns SUCCESS if command aborted else FAILED */ -int +static int scsih_abort(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); @@ -2493,7 +2490,7 @@ scsih_abort(struct scsi_cmnd *scmd) * * Returns SUCCESS if command aborted else FAILED */ -int +static int scsih_dev_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); @@ -2556,7 +2553,7 @@ scsih_dev_reset(struct scsi_cmnd *scmd) * * Returns SUCCESS if command aborted else FAILED */ -int +static int scsih_target_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); @@ -2619,7 +2616,7 @@ scsih_target_reset(struct scsi_cmnd *scmd) * * Returns SUCCESS if command aborted else FAILED */ -int +static int scsih_host_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); @@ -3449,7 +3446,7 @@ _scsih_tm_volume_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, * * Context - processed in interrupt context. */ -void +static void _scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 event, u32 event_context) { @@ -3488,7 +3485,7 @@ _scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 event, * * Context - processed in interrupt context. */ -void +static void _scsih_issue_delayed_sas_io_unit_ctrl(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) { @@ -4026,7 +4023,7 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full */ -int +static int scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -6305,11 +6302,10 @@ _scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num) static void _scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) { - int rc; sdev->no_uld_attach = no_uld_attach ? 1 : 0; sdev_printk(KERN_INFO, sdev, "%s raid component\n", sdev->no_uld_attach ? "hidding" : "exposing"); - rc = scsi_device_reprobe(sdev); + WARN_ON(scsi_device_reprobe(sdev)); } /** @@ -8131,7 +8127,7 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) * Routine called when unloading the driver. * Return nothing. */ -void scsih_remove(struct pci_dev *pdev) +static void scsih_remove(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8204,7 +8200,7 @@ void scsih_remove(struct pci_dev *pdev) * * Return nothing. */ -void +static void scsih_shutdown(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8445,7 +8441,7 @@ _scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc) * of scanning the entire bus. In our implemention, we will kick off * firmware discovery. */ -void +static void scsih_scan_start(struct Scsi_Host *shost) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8472,7 +8468,7 @@ scsih_scan_start(struct Scsi_Host *shost) * scsi_host and the elapsed time of the scan in jiffies. In our implemention, * we wait for firmware discovery to complete, then return 1. */ -int +static int scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8602,7 +8598,7 @@ static struct raid_function_template mpt3sas_raid_functions = { * MPI25_VERSION for SAS 3.0 HBA devices, and * MPI26 VERSION for Cutlass & Invader SAS 3.0 HBA devices */ -u16 +static u16 _scsih_determine_hba_mpi_version(struct pci_dev *pdev) { @@ -8654,7 +8650,7 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev) * * Returns 0 success, anything else error. */ -int +static int _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct MPT3SAS_ADAPTER *ioc; @@ -8863,7 +8859,7 @@ out_add_shost_fail: * * Returns 0 success, anything else error. */ -int +static int scsih_suspend(struct pci_dev *pdev, pm_message_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8890,7 +8886,7 @@ scsih_suspend(struct pci_dev *pdev, pm_message_t state) * * Returns 0 success, anything else error. */ -int +static int scsih_resume(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8927,7 +8923,7 @@ scsih_resume(struct pci_dev *pdev) * Return value: * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT */ -pci_ers_result_t +static pci_ers_result_t scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8964,7 +8960,7 @@ scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) * code after the PCI slot has been reset, just before we * should resume normal operations. */ -pci_ers_result_t +static pci_ers_result_t scsih_pci_slot_reset(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -9000,7 +8996,7 @@ scsih_pci_slot_reset(struct pci_dev *pdev) * OK to resume normal operation. Use completion to allow * halted scsi ops to resume. */ -void +static void scsih_pci_resume(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -9017,7 +9013,7 @@ scsih_pci_resume(struct pci_dev *pdev) * scsih_pci_mmio_enabled - Enable MMIO and dump debug registers * @pdev: pointer to PCI device */ -pci_ers_result_t +static pci_ers_result_t scsih_pci_mmio_enabled(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -9145,7 +9141,7 @@ static struct pci_driver mpt3sas_driver = { * * Returns 0 success, anything else error. */ -int +static int scsih_init(void) { mpt2_ids = 0; @@ -9195,7 +9191,7 @@ scsih_init(void) * * Returns 0 success, anything else error. */ -void +static void scsih_exit(void) { diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c index f58a8c61fdec..b74faf1a69b2 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_transport.c +++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c @@ -300,7 +300,6 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, int rc; u16 smid; u32 ioc_state; - unsigned long timeleft; void *psge; u8 issue_reset = 0; void *data_out = NULL; @@ -394,8 +393,7 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, ioc->name, (unsigned long long)sas_address)); init_completion(&ioc->transport_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); + wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", @@ -1106,7 +1104,6 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, int rc; u16 smid; u32 ioc_state; - unsigned long timeleft; void *psge; u8 issue_reset = 0; void *data_out = NULL; @@ -1202,8 +1199,7 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, phy->number)); init_completion(&ioc->transport_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); + wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", @@ -1419,7 +1415,6 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, int rc; u16 smid; u32 ioc_state; - unsigned long timeleft; void *psge; u8 issue_reset = 0; void *data_out = NULL; @@ -1520,8 +1515,7 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, phy->number, phy_operation)); init_completion(&ioc->transport_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); + wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", @@ -1896,7 +1890,6 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, int rc; u16 smid; u32 ioc_state; - unsigned long timeleft; void *psge; u8 issue_reset = 0; dma_addr_t dma_addr_in = 0; @@ -2040,8 +2033,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, init_completion(&ioc->transport_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); + wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s : timeout\n", From 0d667f72b2a20bbac72bec0ab11467fc70bb0f1f Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Wed, 3 Aug 2016 15:00:18 +0200 Subject: [PATCH 005/173] mpt3sas: Don't spam logs if logging level is 0 In _scsih_io_done() we test if the ioc->logging_level does _not_ have the MPT_DEBUG_REPLY bit set and if it hasn't we print the debug messages. This unfortunately is the wrong way around. Note, the actual bug is older than af0094115 but this commit removed the CONFIG_SCSI_MPT3SAS_LOGGING Kconfig option which hid the bug. Fixes: af0094115 'mpt2sas, mpt3sas: Remove SCSI_MPTXSAS_LOGGING entry from Kconfig' Signed-off-by: Johannes Thumshirn Acked-by: Chaitra P B Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 7732e3ef2db7..209a969a979d 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -4692,7 +4692,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) le16_to_cpu(mpi_reply->DevHandle)); mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq); - if (!(ioc->logging_level & MPT_DEBUG_REPLY) && + if ((ioc->logging_level & MPT_DEBUG_REPLY) && ((scmd->sense_buffer[2] == UNIT_ATTENTION) || (scmd->sense_buffer[2] == MEDIUM_ERROR) || (scmd->sense_buffer[2] == HARDWARE_ERROR))) From 1d48390117c7df9839cf4d43ec33346e7868a53c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Aug 2016 21:42:32 +0300 Subject: [PATCH 006/173] qla2xxx: small cleanup in qla2x00_wait_for_hba_ready() The "if (test_bit(UNLOADING..." line was indented one tab more than it should have been. There was an extra parenthesis around the qla2x00_reset_active() function call. I lined up the conditions a bit so that it shows how they group together. Signed-off-by: Dan Carpenter Acked-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_os.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 2674f4c16bc3..1dd865028edd 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -899,12 +899,12 @@ qla2x00_wait_for_hba_ready(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - while (((qla2x00_reset_active(vha)) || ha->dpc_active || - ha->flags.mbox_busy) || - test_bit(FX00_RESET_RECOVERY, &vha->dpc_flags) || - test_bit(FX00_TARGET_SCAN, &vha->dpc_flags)) { - if (test_bit(UNLOADING, &base_vha->dpc_flags)) - break; + while ((qla2x00_reset_active(vha) || ha->dpc_active || + ha->flags.mbox_busy) || + test_bit(FX00_RESET_RECOVERY, &vha->dpc_flags) || + test_bit(FX00_TARGET_SCAN, &vha->dpc_flags)) { + if (test_bit(UNLOADING, &base_vha->dpc_flags)) + break; msleep(1000); } } From 6c223761eb5482dca2bd981d0a800c4aba3c9009 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Mon, 27 Jun 2016 16:41:00 -0500 Subject: [PATCH 007/173] smartpqi: initial commit of Microsemi smartpqi driver This initial commit contains Microsemi's smartpqi module. [mkp: Minor tweaks to apply to 4.9/scsi-queue] Reviewed-by: Scott Benesh Reviewed-by: Kevin Barnett Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Reviewed-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Reviewed-by: Ewan D. Milne Signed-off-by: Martin K. Petersen --- MAINTAINERS | 11 + drivers/scsi/Kconfig | 1 + drivers/scsi/Makefile | 1 + drivers/scsi/smartpqi/Kconfig | 50 + drivers/scsi/smartpqi/Makefile | 3 + drivers/scsi/smartpqi/smartpqi.h | 1133 +++ drivers/scsi/smartpqi/smartpqi_init.c | 6306 +++++++++++++++++ .../scsi/smartpqi/smartpqi_sas_transport.c | 350 + drivers/scsi/smartpqi/smartpqi_sis.c | 394 + drivers/scsi/smartpqi/smartpqi_sis.h | 32 + 10 files changed, 8281 insertions(+) create mode 100644 drivers/scsi/smartpqi/Kconfig create mode 100644 drivers/scsi/smartpqi/Makefile create mode 100644 drivers/scsi/smartpqi/smartpqi.h create mode 100644 drivers/scsi/smartpqi/smartpqi_init.c create mode 100644 drivers/scsi/smartpqi/smartpqi_sas_transport.c create mode 100644 drivers/scsi/smartpqi/smartpqi_sis.c create mode 100644 drivers/scsi/smartpqi/smartpqi_sis.h diff --git a/MAINTAINERS b/MAINTAINERS index 20bb1d00098c..5f502248ba7b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7819,6 +7819,17 @@ W: http://www.melexis.com S: Supported F: drivers/iio/temperature/mlx90614.c +MICROSEMI SMART ARRAY SMARTPQI DRIVER (smartpqi) +M: Don Brace +L: esc.storagedev@microsemi.com +L: linux-scsi@vger.kernel.org +S: Supported +F: drivers/scsi/smartpqi/smartpqi*.[ch] +F: drivers/scsi/smartpqi/Kconfig +F: drivers/scsi/smartpqi/Makefile +F: include/linux/cciss*.h +F: include/uapi/linux/cciss*.h + MN88472 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 7d1b4317eccc..75de1dc725a1 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -540,6 +540,7 @@ config SCSI_ARCMSR source "drivers/scsi/esas2r/Kconfig" source "drivers/scsi/megaraid/Kconfig.megaraid" source "drivers/scsi/mpt3sas/Kconfig" +source "drivers/scsi/smartpqi/Kconfig" source "drivers/scsi/ufs/Kconfig" config SCSI_HPTIOP diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index d5397987e731..fc0d9b8f2bdf 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -94,6 +94,7 @@ obj-$(CONFIG_SCSI_PAS16) += pas16.o obj-$(CONFIG_SCSI_T128) += t128.o obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o obj-$(CONFIG_SCSI_HPSA) += hpsa.o +obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi/ obj-$(CONFIG_SCSI_DTC3280) += dtc.o obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/ obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o diff --git a/drivers/scsi/smartpqi/Kconfig b/drivers/scsi/smartpqi/Kconfig new file mode 100644 index 000000000000..5d77a804543f --- /dev/null +++ b/drivers/scsi/smartpqi/Kconfig @@ -0,0 +1,50 @@ +# +# Kernel configuration file for the SMARTPQI +# +# Copyright (c) 2016 Microsemi Corporation +# Copyright (c) 2016 PMC-Sierra, Inc. +# (mailto:esc.storagedev@microsemi.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; version 2 +# of the License. + +# 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 + +config SCSI_SMARTPQI + tristate "Microsemi PQI Driver" + default n + depends on PCI && SCSI && !S390 + select SCSI_SAS_ATTRS + select RAID_ATTRS + ---help--- + This driver supports Microsemi PQI controllers. + + + + To compile this driver as a module, choose M here: the + module will be called smartpqi diff --git a/drivers/scsi/smartpqi/Makefile b/drivers/scsi/smartpqi/Makefile new file mode 100644 index 000000000000..0f42a225a664 --- /dev/null +++ b/drivers/scsi/smartpqi/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I. +obj-m += smartpqi.o +smartpqi-objs := smartpqi_init.o smartpqi_sis.o smartpqi_sas_transport.o diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h new file mode 100644 index 000000000000..ea4900588f48 --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -0,0 +1,1133 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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; version 2 of the License. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#if !defined(_SMARTPQI_H) +#define _SMARTPQI_H + +#pragma pack(1) + +#define PQI_DEVICE_SIGNATURE "PQI DREG" + +/* This structure is defined by the PQI specification. */ +struct pqi_device_registers { + __le64 signature; + u8 function_and_status_code; + u8 reserved[7]; + u8 max_admin_iq_elements; + u8 max_admin_oq_elements; + u8 admin_iq_element_length; /* in 16-byte units */ + u8 admin_oq_element_length; /* in 16-byte units */ + __le16 max_reset_timeout; /* in 100-millisecond units */ + u8 reserved1[2]; + __le32 legacy_intx_status; + __le32 legacy_intx_mask_set; + __le32 legacy_intx_mask_clear; + u8 reserved2[28]; + __le32 device_status; + u8 reserved3[4]; + __le64 admin_iq_pi_offset; + __le64 admin_oq_ci_offset; + __le64 admin_iq_element_array_addr; + __le64 admin_oq_element_array_addr; + __le64 admin_iq_ci_addr; + __le64 admin_oq_pi_addr; + u8 admin_iq_num_elements; + u8 admin_oq_num_elements; + __le16 admin_queue_int_msg_num; + u8 reserved4[4]; + __le32 device_error; + u8 reserved5[4]; + __le64 error_details; + __le32 device_reset; + __le32 power_action; + u8 reserved6[104]; +}; + +/* + * controller registers + * + * These are defined by the PMC implementation. + * + * Some registers (those named sis_*) are only used when in + * legacy SIS mode before we transition the controller into + * PQI mode. There are a number of other SIS mode registers, + * but we don't use them, so only the SIS registers that we + * care about are defined here. The offsets mentioned in the + * comments are the offsets from the PCIe BAR 0. + */ +struct pqi_ctrl_registers { + u8 reserved[0x20]; + __le32 sis_host_to_ctrl_doorbell; /* 20h */ + u8 reserved1[0x34 - (0x20 + sizeof(__le32))]; + __le32 sis_interrupt_mask; /* 34h */ + u8 reserved2[0x9c - (0x34 + sizeof(__le32))]; + __le32 sis_ctrl_to_host_doorbell; /* 9Ch */ + u8 reserved3[0xa0 - (0x9c + sizeof(__le32))]; + __le32 sis_ctrl_to_host_doorbell_clear; /* A0h */ + u8 reserved4[0xbc - (0xa0 + sizeof(__le32))]; + __le32 sis_firmware_status; /* BCh */ + u8 reserved5[0x1000 - (0xbc + sizeof(__le32))]; + __le32 sis_mailbox[8]; /* 1000h */ + u8 reserved6[0x4000 - (0x1000 + (sizeof(__le32) * 8))]; + /* + * The PQI spec states that the PQI registers should be at + * offset 0 from the PCIe BAR 0. However, we can't map + * them at offset 0 because that would break compatibility + * with the SIS registers. So we map them at offset 4000h. + */ + struct pqi_device_registers pqi_registers; /* 4000h */ +}; + +#define PQI_DEVICE_REGISTERS_OFFSET 0x4000 + +enum pqi_io_path { + RAID_PATH = 0, + AIO_PATH = 1 +}; + +struct pqi_sg_descriptor { + __le64 address; + __le32 length; + __le32 flags; +}; + +/* manifest constants for the flags field of pqi_sg_descriptor */ +#define CISS_SG_LAST 0x40000000 +#define CISS_SG_CHAIN 0x80000000 + +struct pqi_iu_header { + u8 iu_type; + u8 reserved; + __le16 iu_length; /* in bytes - does not include the length */ + /* of this header */ + __le16 response_queue_id; /* specifies the OQ where the */ + /* response IU is to be delivered */ + u8 work_area[2]; /* reserved for driver use */ +}; + +/* + * According to the PQI spec, the IU header is only the first 4 bytes of our + * pqi_iu_header structure. + */ +#define PQI_REQUEST_HEADER_LENGTH 4 + +struct pqi_general_admin_request { + struct pqi_iu_header header; + __le16 request_id; + u8 function_code; + union { + struct { + u8 reserved[33]; + __le32 buffer_length; + struct pqi_sg_descriptor sg_descriptor; + } report_device_capability; + + struct { + u8 reserved; + __le16 queue_id; + u8 reserved1[2]; + __le64 element_array_addr; + __le64 ci_addr; + __le16 num_elements; + __le16 element_length; + u8 queue_protocol; + u8 reserved2[23]; + __le32 vendor_specific; + } create_operational_iq; + + struct { + u8 reserved; + __le16 queue_id; + u8 reserved1[2]; + __le64 element_array_addr; + __le64 pi_addr; + __le16 num_elements; + __le16 element_length; + u8 queue_protocol; + u8 reserved2[3]; + __le16 int_msg_num; + __le16 coalescing_count; + __le32 min_coalescing_time; + __le32 max_coalescing_time; + u8 reserved3[8]; + __le32 vendor_specific; + } create_operational_oq; + + struct { + u8 reserved; + __le16 queue_id; + u8 reserved1[50]; + } delete_operational_queue; + + struct { + u8 reserved; + __le16 queue_id; + u8 reserved1[46]; + __le32 vendor_specific; + } change_operational_iq_properties; + + } data; +}; + +struct pqi_general_admin_response { + struct pqi_iu_header header; + __le16 request_id; + u8 function_code; + u8 status; + union { + struct { + u8 status_descriptor[4]; + __le64 iq_pi_offset; + u8 reserved[40]; + } create_operational_iq; + + struct { + u8 status_descriptor[4]; + __le64 oq_ci_offset; + u8 reserved[40]; + } create_operational_oq; + } data; +}; + +struct pqi_iu_layer_descriptor { + u8 inbound_spanning_supported : 1; + u8 reserved : 7; + u8 reserved1[5]; + __le16 max_inbound_iu_length; + u8 outbound_spanning_supported : 1; + u8 reserved2 : 7; + u8 reserved3[5]; + __le16 max_outbound_iu_length; +}; + +struct pqi_device_capability { + __le16 data_length; + u8 reserved[6]; + u8 iq_arbitration_priority_support_bitmask; + u8 maximum_aw_a; + u8 maximum_aw_b; + u8 maximum_aw_c; + u8 max_arbitration_burst : 3; + u8 reserved1 : 4; + u8 iqa : 1; + u8 reserved2[2]; + u8 iq_freeze : 1; + u8 reserved3 : 7; + __le16 max_inbound_queues; + __le16 max_elements_per_iq; + u8 reserved4[4]; + __le16 max_iq_element_length; + __le16 min_iq_element_length; + u8 reserved5[2]; + __le16 max_outbound_queues; + __le16 max_elements_per_oq; + __le16 intr_coalescing_time_granularity; + __le16 max_oq_element_length; + __le16 min_oq_element_length; + u8 reserved6[24]; + struct pqi_iu_layer_descriptor iu_layer_descriptors[32]; +}; + +#define PQI_MAX_EMBEDDED_SG_DESCRIPTORS 4 + +struct pqi_raid_path_request { + struct pqi_iu_header header; + __le16 request_id; + __le16 nexus_id; + __le32 buffer_length; + u8 lun_number[8]; + __le16 protocol_specific; + u8 data_direction : 2; + u8 partial : 1; + u8 reserved1 : 4; + u8 fence : 1; + __le16 error_index; + u8 reserved2; + u8 task_attribute : 3; + u8 command_priority : 4; + u8 reserved3 : 1; + u8 reserved4 : 2; + u8 additional_cdb_bytes_usage : 3; + u8 reserved5 : 3; + u8 cdb[32]; + struct pqi_sg_descriptor + sg_descriptors[PQI_MAX_EMBEDDED_SG_DESCRIPTORS]; +}; + +struct pqi_aio_path_request { + struct pqi_iu_header header; + __le16 request_id; + u8 reserved1[2]; + __le32 nexus_id; + __le32 buffer_length; + u8 data_direction : 2; + u8 partial : 1; + u8 memory_type : 1; + u8 fence : 1; + u8 encryption_enable : 1; + u8 reserved2 : 2; + u8 task_attribute : 3; + u8 command_priority : 4; + u8 reserved3 : 1; + __le16 data_encryption_key_index; + __le32 encrypt_tweak_lower; + __le32 encrypt_tweak_upper; + u8 cdb[16]; + __le16 error_index; + u8 num_sg_descriptors; + u8 cdb_length; + u8 lun_number[8]; + u8 reserved4[4]; + struct pqi_sg_descriptor + sg_descriptors[PQI_MAX_EMBEDDED_SG_DESCRIPTORS]; +}; + +struct pqi_io_response { + struct pqi_iu_header header; + __le16 request_id; + __le16 error_index; + u8 reserved2[4]; +}; + +struct pqi_general_management_request { + struct pqi_iu_header header; + __le16 request_id; + union { + struct { + u8 reserved[2]; + __le32 buffer_length; + struct pqi_sg_descriptor sg_descriptors[3]; + } report_event_configuration; + + struct { + __le16 global_event_oq_id; + __le32 buffer_length; + struct pqi_sg_descriptor sg_descriptors[3]; + } set_event_configuration; + } data; +}; + +struct pqi_event_descriptor { + u8 event_type; + u8 reserved; + __le16 oq_id; +}; + +struct pqi_event_config { + u8 reserved[2]; + u8 num_event_descriptors; + u8 reserved1; + struct pqi_event_descriptor descriptors[1]; +}; + +#define PQI_MAX_EVENT_DESCRIPTORS 255 + +struct pqi_event_response { + struct pqi_iu_header header; + u8 event_type; + u8 reserved2 : 7; + u8 request_acknowlege : 1; + __le16 event_id; + __le32 additional_event_id; + u8 data[16]; +}; + +struct pqi_event_acknowledge_request { + struct pqi_iu_header header; + u8 event_type; + u8 reserved2; + __le16 event_id; + __le32 additional_event_id; +}; + +struct pqi_task_management_request { + struct pqi_iu_header header; + __le16 request_id; + __le16 nexus_id; + u8 reserved[4]; + u8 lun_number[8]; + __le16 protocol_specific; + __le16 outbound_queue_id_to_manage; + __le16 request_id_to_manage; + u8 task_management_function; + u8 reserved2 : 7; + u8 fence : 1; +}; + +#define SOP_TASK_MANAGEMENT_LUN_RESET 0x8 +#define PQI_ABORT_TIMEOUT_MSECS (20 * 1000) + +struct pqi_task_management_response { + struct pqi_iu_header header; + __le16 request_id; + __le16 nexus_id; + u8 additional_response_info[3]; + u8 response_code; +}; + +struct pqi_aio_error_info { + u8 status; + u8 service_response; + u8 data_present; + u8 reserved; + __le32 residual_count; + __le16 data_length; + __le16 reserved1; + u8 data[256]; +}; + +struct pqi_raid_error_info { + u8 data_in_result; + u8 data_out_result; + u8 reserved[3]; + u8 status; + __le16 status_qualifier; + __le16 sense_data_length; + __le16 response_data_length; + __le32 data_in_transferred; + __le32 data_out_transferred; + u8 data[256]; +}; + +#define PQI_REQUEST_IU_TASK_MANAGEMENT 0x13 +#define PQI_REQUEST_IU_RAID_PATH_IO 0x14 +#define PQI_REQUEST_IU_AIO_PATH_IO 0x15 +#define PQI_REQUEST_IU_GENERAL_ADMIN 0x60 +#define PQI_REQUEST_IU_REPORT_VENDOR_EVENT_CONFIG 0x72 +#define PQI_REQUEST_IU_SET_VENDOR_EVENT_CONFIG 0x73 +#define PQI_REQUEST_IU_ACKNOWLEDGE_VENDOR_EVENT 0xf6 + +#define PQI_RESPONSE_IU_GENERAL_MANAGEMENT 0x81 +#define PQI_RESPONSE_IU_TASK_MANAGEMENT 0x93 +#define PQI_RESPONSE_IU_GENERAL_ADMIN 0xe0 +#define PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS 0xf0 +#define PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS 0xf1 +#define PQI_RESPONSE_IU_RAID_PATH_IO_ERROR 0xf2 +#define PQI_RESPONSE_IU_AIO_PATH_IO_ERROR 0xf3 +#define PQI_RESPONSE_IU_AIO_PATH_DISABLED 0xf4 +#define PQI_RESPONSE_IU_VENDOR_EVENT 0xf5 + +#define PQI_GENERAL_ADMIN_FUNCTION_REPORT_DEVICE_CAPABILITY 0x0 +#define PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ 0x10 +#define PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ 0x11 +#define PQI_GENERAL_ADMIN_FUNCTION_DELETE_IQ 0x12 +#define PQI_GENERAL_ADMIN_FUNCTION_DELETE_OQ 0x13 +#define PQI_GENERAL_ADMIN_FUNCTION_CHANGE_IQ_PROPERTY 0x14 + +#define PQI_GENERAL_ADMIN_STATUS_SUCCESS 0x0 + +#define PQI_IQ_PROPERTY_IS_AIO_QUEUE 0x1 + +#define PQI_GENERAL_ADMIN_IU_LENGTH 0x3c +#define PQI_PROTOCOL_SOP 0x0 + +#define PQI_DATA_IN_OUT_GOOD 0x0 +#define PQI_DATA_IN_OUT_UNDERFLOW 0x1 +#define PQI_DATA_IN_OUT_BUFFER_ERROR 0x40 +#define PQI_DATA_IN_OUT_BUFFER_OVERFLOW 0x41 +#define PQI_DATA_IN_OUT_BUFFER_OVERFLOW_DESCRIPTOR_AREA 0x42 +#define PQI_DATA_IN_OUT_BUFFER_OVERFLOW_BRIDGE 0x43 +#define PQI_DATA_IN_OUT_PCIE_FABRIC_ERROR 0x60 +#define PQI_DATA_IN_OUT_PCIE_COMPLETION_TIMEOUT 0x61 +#define PQI_DATA_IN_OUT_PCIE_COMPLETER_ABORT_RECEIVED 0x62 +#define PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST_RECEIVED 0x63 +#define PQI_DATA_IN_OUT_PCIE_ECRC_CHECK_FAILED 0x64 +#define PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST 0x65 +#define PQI_DATA_IN_OUT_PCIE_ACS_VIOLATION 0x66 +#define PQI_DATA_IN_OUT_PCIE_TLP_PREFIX_BLOCKED 0x67 +#define PQI_DATA_IN_OUT_PCIE_POISONED_MEMORY_READ 0x6F +#define PQI_DATA_IN_OUT_ERROR 0xf0 +#define PQI_DATA_IN_OUT_PROTOCOL_ERROR 0xf1 +#define PQI_DATA_IN_OUT_HARDWARE_ERROR 0xf2 +#define PQI_DATA_IN_OUT_UNSOLICITED_ABORT 0xf3 +#define PQI_DATA_IN_OUT_ABORTED 0xf4 +#define PQI_DATA_IN_OUT_TIMEOUT 0xf5 + +#define CISS_CMD_STATUS_SUCCESS 0x0 +#define CISS_CMD_STATUS_TARGET_STATUS 0x1 +#define CISS_CMD_STATUS_DATA_UNDERRUN 0x2 +#define CISS_CMD_STATUS_DATA_OVERRUN 0x3 +#define CISS_CMD_STATUS_INVALID 0x4 +#define CISS_CMD_STATUS_PROTOCOL_ERROR 0x5 +#define CISS_CMD_STATUS_HARDWARE_ERROR 0x6 +#define CISS_CMD_STATUS_CONNECTION_LOST 0x7 +#define CISS_CMD_STATUS_ABORTED 0x8 +#define CISS_CMD_STATUS_ABORT_FAILED 0x9 +#define CISS_CMD_STATUS_UNSOLICITED_ABORT 0xa +#define CISS_CMD_STATUS_TIMEOUT 0xb +#define CISS_CMD_STATUS_UNABORTABLE 0xc +#define CISS_CMD_STATUS_TMF 0xd +#define CISS_CMD_STATUS_AIO_DISABLED 0xe + +#define PQI_NUM_EVENT_QUEUE_ELEMENTS 32 +#define PQI_EVENT_OQ_ELEMENT_LENGTH sizeof(struct pqi_event_response) + +#define PQI_EVENT_TYPE_HOTPLUG 0x1 +#define PQI_EVENT_TYPE_HARDWARE 0x2 +#define PQI_EVENT_TYPE_PHYSICAL_DEVICE 0x4 +#define PQI_EVENT_TYPE_LOGICAL_DEVICE 0x5 +#define PQI_EVENT_TYPE_AIO_STATE_CHANGE 0xfd +#define PQI_EVENT_TYPE_AIO_CONFIG_CHANGE 0xfe +#define PQI_EVENT_TYPE_HEARTBEAT 0xff + +#pragma pack() + +#define PQI_ERROR_BUFFER_ELEMENT_LENGTH \ + sizeof(struct pqi_raid_error_info) + +/* these values are based on our implementation */ +#define PQI_ADMIN_IQ_NUM_ELEMENTS 8 +#define PQI_ADMIN_OQ_NUM_ELEMENTS 20 +#define PQI_ADMIN_IQ_ELEMENT_LENGTH 64 +#define PQI_ADMIN_OQ_ELEMENT_LENGTH 64 + +#define PQI_OPERATIONAL_IQ_ELEMENT_LENGTH 128 +#define PQI_OPERATIONAL_OQ_ELEMENT_LENGTH 16 + +#define PQI_MIN_MSIX_VECTORS 1 +#define PQI_MAX_MSIX_VECTORS 64 + +/* these values are defined by the PQI spec */ +#define PQI_MAX_NUM_ELEMENTS_ADMIN_QUEUE 255 +#define PQI_MAX_NUM_ELEMENTS_OPERATIONAL_QUEUE 65535 +#define PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT 64 +#define PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT 16 +#define PQI_ADMIN_INDEX_ALIGNMENT 64 +#define PQI_OPERATIONAL_INDEX_ALIGNMENT 4 + +#define PQI_MIN_OPERATIONAL_QUEUE_ID 1 +#define PQI_MAX_OPERATIONAL_QUEUE_ID 65535 + +#define PQI_AIO_SERV_RESPONSE_COMPLETE 0 +#define PQI_AIO_SERV_RESPONSE_FAILURE 1 +#define PQI_AIO_SERV_RESPONSE_TMF_COMPLETE 2 +#define PQI_AIO_SERV_RESPONSE_TMF_SUCCEEDED 3 +#define PQI_AIO_SERV_RESPONSE_TMF_REJECTED 4 +#define PQI_AIO_SERV_RESPONSE_TMF_INCORRECT_LUN 5 + +#define PQI_AIO_STATUS_IO_ERROR 0x1 +#define PQI_AIO_STATUS_IO_ABORTED 0x2 +#define PQI_AIO_STATUS_NO_PATH_TO_DEVICE 0x3 +#define PQI_AIO_STATUS_INVALID_DEVICE 0x4 +#define PQI_AIO_STATUS_AIO_PATH_DISABLED 0xe +#define PQI_AIO_STATUS_UNDERRUN 0x51 +#define PQI_AIO_STATUS_OVERRUN 0x75 + +typedef u32 pqi_index_t; + +/* SOP data direction flags */ +#define SOP_NO_DIRECTION_FLAG 0 +#define SOP_WRITE_FLAG 1 /* host writes data to Data-Out */ + /* buffer */ +#define SOP_READ_FLAG 2 /* host receives data from Data-In */ + /* buffer */ +#define SOP_BIDIRECTIONAL 3 /* data is transferred from the */ + /* Data-Out buffer and data is */ + /* transferred to the Data-In buffer */ + +#define SOP_TASK_ATTRIBUTE_SIMPLE 0 +#define SOP_TASK_ATTRIBUTE_HEAD_OF_QUEUE 1 +#define SOP_TASK_ATTRIBUTE_ORDERED 2 +#define SOP_TASK_ATTRIBUTE_ACA 4 + +#define SOP_TASK_MANAGEMENT_FUNCTION_COMPLETE 0x0 +#define SOP_TASK_MANAGEMENT_FUNCTION_REJECTED 0x4 +#define SOP_TASK_MANAGEMENT_FUNCTION_FAILED 0x5 +#define SOP_TASK_MANAGEMENT_FUNCTION_SUCCEEDED 0x8 + +/* additional CDB bytes usage field codes */ +#define SOP_ADDITIONAL_CDB_BYTES_0 0 /* 16-byte CDB */ +#define SOP_ADDITIONAL_CDB_BYTES_4 1 /* 20-byte CDB */ +#define SOP_ADDITIONAL_CDB_BYTES_8 2 /* 24-byte CDB */ +#define SOP_ADDITIONAL_CDB_BYTES_12 3 /* 28-byte CDB */ +#define SOP_ADDITIONAL_CDB_BYTES_16 4 /* 32-byte CDB */ + +/* + * The purpose of this structure is to obtain proper alignment of objects in + * an admin queue pair. + */ +struct pqi_admin_queues_aligned { + __aligned(PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT) + u8 iq_element_array[PQI_ADMIN_IQ_ELEMENT_LENGTH] + [PQI_ADMIN_IQ_NUM_ELEMENTS]; + __aligned(PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT) + u8 oq_element_array[PQI_ADMIN_OQ_ELEMENT_LENGTH] + [PQI_ADMIN_OQ_NUM_ELEMENTS]; + __aligned(PQI_ADMIN_INDEX_ALIGNMENT) pqi_index_t iq_ci; + __aligned(PQI_ADMIN_INDEX_ALIGNMENT) pqi_index_t oq_pi; +}; + +struct pqi_admin_queues { + void *iq_element_array; + void *oq_element_array; + volatile pqi_index_t *iq_ci; + volatile pqi_index_t *oq_pi; + dma_addr_t iq_element_array_bus_addr; + dma_addr_t oq_element_array_bus_addr; + dma_addr_t iq_ci_bus_addr; + dma_addr_t oq_pi_bus_addr; + __le32 __iomem *iq_pi; + pqi_index_t iq_pi_copy; + __le32 __iomem *oq_ci; + pqi_index_t oq_ci_copy; + struct task_struct *task; + u16 int_msg_num; +}; + +struct pqi_queue_group { + struct pqi_ctrl_info *ctrl_info; /* backpointer */ + u16 iq_id[2]; + u16 oq_id; + u16 int_msg_num; + void *iq_element_array[2]; + void *oq_element_array; + dma_addr_t iq_element_array_bus_addr[2]; + dma_addr_t oq_element_array_bus_addr; + __le32 __iomem *iq_pi[2]; + pqi_index_t iq_pi_copy[2]; + volatile pqi_index_t *iq_ci[2]; + volatile pqi_index_t *oq_pi; + dma_addr_t iq_ci_bus_addr[2]; + dma_addr_t oq_pi_bus_addr; + __le32 __iomem *oq_ci; + pqi_index_t oq_ci_copy; + spinlock_t submit_lock[2]; /* protect submission queue */ + struct list_head request_list[2]; +}; + +struct pqi_event_queue { + u16 oq_id; + u16 int_msg_num; + void *oq_element_array; + volatile pqi_index_t *oq_pi; + dma_addr_t oq_element_array_bus_addr; + dma_addr_t oq_pi_bus_addr; + __le32 __iomem *oq_ci; + pqi_index_t oq_ci_copy; +}; + +#define PQI_DEFAULT_QUEUE_GROUP 0 +#define PQI_MAX_QUEUE_GROUPS PQI_MAX_MSIX_VECTORS + +struct pqi_encryption_info { + u16 data_encryption_key_index; + u32 encrypt_tweak_lower; + u32 encrypt_tweak_upper; +}; + +#define PQI_MAX_OUTSTANDING_REQUESTS ((u32)~0) +#define PQI_MAX_TRANSFER_SIZE (4 * 1024U * 1024U) + +#define RAID_MAP_MAX_ENTRIES 1024 + +#define PQI_RESERVED_IO_SLOTS_LUN_RESET 1 +#define PQI_RESERVED_IO_SLOTS_EVENT_ACK 1 +#define PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS 3 +#define PQI_RESERVED_IO_SLOTS \ + (PQI_RESERVED_IO_SLOTS_LUN_RESET + PQI_RESERVED_IO_SLOTS_EVENT_ACK + \ + PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS) + +#define PQI_PHYSICAL_DEVICE_BUS 0 +#define PQI_RAID_VOLUME_BUS 1 +#define PQI_HBA_BUS 2 +#define PQI_MAX_BUS PQI_HBA_BUS + +#pragma pack(1) + +struct report_lun_header { + __be32 list_length; + u8 extended_response; + u8 reserved[3]; +}; + +struct report_log_lun_extended_entry { + u8 lunid[8]; + u8 volume_id[16]; +}; + +struct report_log_lun_extended { + struct report_lun_header header; + struct report_log_lun_extended_entry lun_entries[1]; +}; + +struct report_phys_lun_extended_entry { + u8 lunid[8]; + __be64 wwid; + u8 device_type; + u8 device_flags; + u8 lun_count; /* number of LUNs in a multi-LUN device */ + u8 redundant_paths; + u32 aio_handle; +}; + +/* for device_flags field of struct report_phys_lun_extended_entry */ +#define REPORT_PHYS_LUN_DEV_FLAG_NON_DISK 0x1 +#define REPORT_PHYS_LUN_DEV_FLAG_AIO_ENABLED 0x8 + +struct report_phys_lun_extended { + struct report_lun_header header; + struct report_phys_lun_extended_entry lun_entries[1]; +}; + +struct raid_map_disk_data { + u32 aio_handle; + u8 xor_mult[2]; + u8 reserved[2]; +}; + +/* constants for flags field of RAID map */ +#define RAID_MAP_ENCRYPTION_ENABLED 0x1 + +struct raid_map { + __le32 structure_size; /* size of entire structure in bytes */ + __le32 volume_blk_size; /* bytes / block in the volume */ + __le64 volume_blk_cnt; /* logical blocks on the volume */ + u8 phys_blk_shift; /* shift factor to convert between */ + /* units of logical blocks and */ + /* physical disk blocks */ + u8 parity_rotation_shift; /* shift factor to convert between */ + /* units of logical stripes and */ + /* physical stripes */ + __le16 strip_size; /* blocks used on each disk / stripe */ + __le64 disk_starting_blk; /* first disk block used in volume */ + __le64 disk_blk_cnt; /* disk blocks used by volume / disk */ + __le16 data_disks_per_row; /* data disk entries / row in the map */ + __le16 metadata_disks_per_row; /* mirror/parity disk entries / row */ + /* in the map */ + __le16 row_cnt; /* rows in each layout map */ + __le16 layout_map_count; /* layout maps (1 map per */ + /* mirror parity group) */ + __le16 flags; + __le16 data_encryption_key_index; + u8 reserved[16]; + struct raid_map_disk_data disk_data[RAID_MAP_MAX_ENTRIES]; +}; + +#pragma pack() + +#define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0" + +struct pqi_scsi_dev { + int devtype; /* as reported by INQUIRY commmand */ + u8 device_type; /* as reported by */ + /* BMIC_IDENTIFY_PHYSICAL_DEVICE */ + /* only valid for devtype = TYPE_DISK */ + int bus; + int target; + int lun; + u8 scsi3addr[8]; + __be64 wwid; + u8 volume_id[16]; + u8 is_physical_device : 1; + u8 target_lun_valid : 1; + u8 expose_device : 1; + u8 no_uld_attach : 1; + u8 aio_enabled : 1; /* only valid for physical disks */ + u8 device_gone : 1; + u8 new_device : 1; + u8 keep_device : 1; + u8 volume_offline : 1; + u8 vendor[8]; /* bytes 8-15 of inquiry data */ + u8 model[16]; /* bytes 16-31 of inquiry data */ + u64 sas_address; + u8 raid_level; + u16 queue_depth; /* max. queue_depth for this device */ + u16 advertised_queue_depth; + u32 aio_handle; + u8 volume_status; + u8 active_path_index; + u8 path_map; + u8 bay; + u8 box[8]; + u16 phys_connector[8]; + int offload_configured; /* I/O accel RAID offload configured */ + int offload_enabled; /* I/O accel RAID offload enabled */ + int offload_enabled_pending; + int offload_to_mirror; /* Send next I/O accelerator RAID */ + /* offload request to mirror drive. */ + struct raid_map *raid_map; /* I/O accelerator RAID map */ + + struct pqi_sas_port *sas_port; + struct scsi_device *sdev; + bool reset_in_progress; + + struct list_head scsi_device_list_entry; + struct list_head new_device_list_entry; + struct list_head add_list_entry; + struct list_head delete_list_entry; +}; + +/* VPD inquiry pages */ +#define SCSI_VPD_SUPPORTED_PAGES 0x0 /* standard page */ +#define SCSI_VPD_DEVICE_ID 0x83 /* standard page */ +#define CISS_VPD_LV_DEVICE_GEOMETRY 0xc1 /* vendor-specific page */ +#define CISS_VPD_LV_OFFLOAD_STATUS 0xc2 /* vendor-specific page */ +#define CISS_VPD_LV_STATUS 0xc3 /* vendor-specific page */ + +#define VPD_PAGE (1 << 8) + +#pragma pack(1) + +/* structure for CISS_VPD_LV_STATUS */ +struct ciss_vpd_logical_volume_status { + u8 peripheral_info; + u8 page_code; + u8 reserved; + u8 page_length; + u8 volume_status; + u8 reserved2[3]; + __be32 flags; +}; + +#pragma pack() + +/* constants for volume_status field of ciss_vpd_logical_volume_status */ +#define CISS_LV_OK 0 +#define CISS_LV_FAILED 1 +#define CISS_LV_NOT_CONFIGURED 2 +#define CISS_LV_DEGRADED 3 +#define CISS_LV_READY_FOR_RECOVERY 4 +#define CISS_LV_UNDERGOING_RECOVERY 5 +#define CISS_LV_WRONG_PHYSICAL_DRIVE_REPLACED 6 +#define CISS_LV_PHYSICAL_DRIVE_CONNECTION_PROBLEM 7 +#define CISS_LV_HARDWARE_OVERHEATING 8 +#define CISS_LV_HARDWARE_HAS_OVERHEATED 9 +#define CISS_LV_UNDERGOING_EXPANSION 10 +#define CISS_LV_NOT_AVAILABLE 11 +#define CISS_LV_QUEUED_FOR_EXPANSION 12 +#define CISS_LV_DISABLED_SCSI_ID_CONFLICT 13 +#define CISS_LV_EJECTED 14 +#define CISS_LV_UNDERGOING_ERASE 15 +/* state 16 not used */ +#define CISS_LV_READY_FOR_PREDICTIVE_SPARE_REBUILD 17 +#define CISS_LV_UNDERGOING_RPI 18 +#define CISS_LV_PENDING_RPI 19 +#define CISS_LV_ENCRYPTED_NO_KEY 20 +/* state 21 not used */ +#define CISS_LV_UNDERGOING_ENCRYPTION 22 +#define CISS_LV_UNDERGOING_ENCRYPTION_REKEYING 23 +#define CISS_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER 24 +#define CISS_LV_PENDING_ENCRYPTION 25 +#define CISS_LV_PENDING_ENCRYPTION_REKEYING 26 +#define CISS_LV_NOT_SUPPORTED 27 +#define CISS_LV_STATUS_UNAVAILABLE 255 + +/* constants for flags field of ciss_vpd_logical_volume_status */ +#define CISS_LV_FLAGS_NO_HOST_IO 0x1 /* volume not available for */ + /* host I/O */ + +/* for SAS hosts and SAS expanders */ +struct pqi_sas_node { + struct device *parent_dev; + struct list_head port_list_head; +}; + +struct pqi_sas_port { + struct list_head port_list_entry; + u64 sas_address; + struct sas_port *port; + int next_phy_index; + struct list_head phy_list_head; + struct pqi_sas_node *parent_node; + struct sas_rphy *rphy; +}; + +struct pqi_sas_phy { + struct list_head phy_list_entry; + struct sas_phy *phy; + struct pqi_sas_port *parent_port; + bool added_to_port; +}; + +struct pqi_io_request { + atomic_t refcount; + u16 index; + void (*io_complete_callback)(struct pqi_io_request *io_request, + void *context); + void *context; + int status; + struct scsi_cmnd *scmd; + void *error_info; + struct pqi_sg_descriptor *sg_chain_buffer; + dma_addr_t sg_chain_buffer_dma_handle; + void *iu; + struct list_head request_list_entry; +}; + +/* for indexing into the pending_events[] field of struct pqi_ctrl_info */ +#define PQI_EVENT_HEARTBEAT 0 +#define PQI_EVENT_HOTPLUG 1 +#define PQI_EVENT_HARDWARE 2 +#define PQI_EVENT_PHYSICAL_DEVICE 3 +#define PQI_EVENT_LOGICAL_DEVICE 4 +#define PQI_EVENT_AIO_STATE_CHANGE 5 +#define PQI_EVENT_AIO_CONFIG_CHANGE 6 +#define PQI_NUM_SUPPORTED_EVENTS 7 + +struct pqi_event { + bool pending; + u8 event_type; + __le16 event_id; + __le32 additional_event_id; +}; + +struct pqi_ctrl_info { + unsigned int ctrl_id; + struct pci_dev *pci_dev; + char firmware_version[11]; + void __iomem *iomem_base; + struct pqi_ctrl_registers __iomem *registers; + struct pqi_device_registers __iomem *pqi_registers; + u32 max_sg_entries; + u32 config_table_offset; + u32 config_table_length; + u16 max_inbound_queues; + u16 max_elements_per_iq; + u16 max_iq_element_length; + u16 max_outbound_queues; + u16 max_elements_per_oq; + u16 max_oq_element_length; + u32 max_transfer_size; + u32 max_outstanding_requests; + u32 max_io_slots; + unsigned int scsi_ml_can_queue; + unsigned short sg_tablesize; + unsigned int max_sectors; + u32 error_buffer_length; + void *error_buffer; + dma_addr_t error_buffer_dma_handle; + size_t sg_chain_buffer_length; + unsigned int num_queue_groups; + unsigned int num_active_queue_groups; + u16 num_elements_per_iq; + u16 num_elements_per_oq; + u16 max_inbound_iu_length_per_firmware; + u16 max_inbound_iu_length; + unsigned int max_sg_per_iu; + void *admin_queue_memory_base; + u32 admin_queue_memory_length; + dma_addr_t admin_queue_memory_base_dma_handle; + void *queue_memory_base; + u32 queue_memory_length; + dma_addr_t queue_memory_base_dma_handle; + struct pqi_admin_queues admin_queues; + struct pqi_queue_group queue_groups[PQI_MAX_QUEUE_GROUPS]; + struct pqi_event_queue event_queue; + int max_msix_vectors; + int num_msix_vectors_enabled; + int num_msix_vectors_initialized; + u32 msix_vectors[PQI_MAX_MSIX_VECTORS]; + void *intr_data[PQI_MAX_MSIX_VECTORS]; + int event_irq; + struct Scsi_Host *scsi_host; + + struct mutex scan_mutex; + u8 inbound_spanning_supported : 1; + u8 outbound_spanning_supported : 1; + u8 pqi_mode_enabled : 1; + u8 controller_online : 1; + u8 heartbeat_timer_started : 1; + + struct list_head scsi_device_list; + spinlock_t scsi_device_list_lock; + + struct delayed_work rescan_work; + struct delayed_work update_time_work; + + struct pqi_sas_node *sas_host; + u64 sas_address; + + struct pqi_io_request *io_request_pool; + u16 next_io_request_slot; + + struct pqi_event pending_events[PQI_NUM_SUPPORTED_EVENTS]; + struct work_struct event_work; + + atomic_t num_interrupts; + int previous_num_interrupts; + unsigned int num_heartbeats_requested; + struct timer_list heartbeat_timer; + + struct semaphore sync_request_sem; + struct semaphore lun_reset_sem; +}; + +/* + * assume worst case: SATA queue depth of 31 minus 4 internal firmware commands + */ +#define PQI_PHYSICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH 27 + +/* 0 = no limit */ +#define PQI_LOGICAL_DRIVE_DEFAULT_MAX_QUEUE_DEPTH 0 + +/* CISS commands */ +#define CISS_READ 0xc0 +#define CISS_REPORT_LOG 0xc2 /* Report Logical LUNs */ +#define CISS_REPORT_PHYS 0xc3 /* Report Physical LUNs */ +#define CISS_GET_RAID_MAP 0xc8 + +/* constants for CISS_REPORT_LOG/CISS_REPORT_PHYS commands */ +#define CISS_REPORT_LOG_EXTENDED 0x1 +#define CISS_REPORT_PHYS_EXTENDED 0x2 + +/* BMIC commands */ +#define BMIC_IDENTIFY_CONTROLLER 0x11 +#define BMIC_IDENTIFY_PHYSICAL_DEVICE 0x15 +#define BMIC_READ 0x26 +#define BMIC_WRITE 0x27 +#define BMIC_SENSE_CONTROLLER_PARAMETERS 0x64 +#define BMIC_SENSE_SUBSYSTEM_INFORMATION 0x66 +#define BMIC_WRITE_HOST_WELLNESS 0xa5 +#define BMIC_CACHE_FLUSH 0xc2 + +#define SA_CACHE_FLUSH 0x01 + +#define MASKED_DEVICE(lunid) ((lunid)[3] & 0xc0) +#define CISS_GET_BUS(lunid) ((lunid)[7] & 0x3f) +#define CISS_GET_LEVEL_2_TARGET(lunid) ((lunid)[6]) +#define CISS_GET_DRIVE_NUMBER(lunid) \ + (((CISS_GET_BUS((lunid)) - 1) << 8) + \ + CISS_GET_LEVEL_2_TARGET((lunid))) + +#define NO_TIMEOUT ((unsigned long) -1) + +#pragma pack(1) + +struct bmic_identify_controller { + u8 configured_logical_drive_count; + __le32 configuration_signature; + u8 firmware_version[4]; + u8 reserved[145]; + __le16 extended_logical_unit_count; + u8 reserved1[34]; + __le16 firmware_build_number; + u8 reserved2[100]; + u8 controller_mode; + u8 reserved3[32]; +}; + +struct bmic_identify_physical_device { + u8 scsi_bus; /* SCSI Bus number on controller */ + u8 scsi_id; /* SCSI ID on this bus */ + __le16 block_size; /* sector size in bytes */ + __le32 total_blocks; /* number for sectors on drive */ + __le32 reserved_blocks; /* controller reserved (RIS) */ + u8 model[40]; /* Physical Drive Model */ + u8 serial_number[40]; /* Drive Serial Number */ + u8 firmware_revision[8]; /* drive firmware revision */ + u8 scsi_inquiry_bits; /* inquiry byte 7 bits */ + u8 compaq_drive_stamp; /* 0 means drive not stamped */ + u8 last_failure_reason; + u8 flags; + u8 more_flags; + u8 scsi_lun; /* SCSI LUN for phys drive */ + u8 yet_more_flags; + u8 even_more_flags; + __le32 spi_speed_rules; + u8 phys_connector[2]; /* connector number on controller */ + u8 phys_box_on_bus; /* phys enclosure this drive resides */ + u8 phys_bay_in_box; /* phys drv bay this drive resides */ + __le32 rpm; /* drive rotational speed in RPM */ + u8 device_type; /* type of drive */ + u8 sata_version; /* only valid when device_type = */ + /* BMIC_DEVICE_TYPE_SATA */ + __le64 big_total_block_count; + __le64 ris_starting_lba; + __le32 ris_size; + u8 wwid[20]; + u8 controller_phy_map[32]; + __le16 phy_count; + u8 phy_connected_dev_type[256]; + u8 phy_to_drive_bay_num[256]; + __le16 phy_to_attached_dev_index[256]; + u8 box_index; + u8 reserved; + __le16 extra_physical_drive_flags; + u8 negotiated_link_rate[256]; + u8 phy_to_phy_map[256]; + u8 redundant_path_present_map; + u8 redundant_path_failure_map; + u8 active_path_number; + __le16 alternate_paths_phys_connector[8]; + u8 alternate_paths_phys_box_on_port[8]; + u8 multi_lun_device_lun_count; + u8 minimum_good_fw_revision[8]; + u8 unique_inquiry_bytes[20]; + u8 current_temperature_degreesC; + u8 temperature_threshold_degreesC; + u8 max_temperature_degreesC; + u8 logical_blocks_per_phys_block_exp; + __le16 current_queue_depth_limit; + u8 switch_name[10]; + __le16 switch_port; + u8 alternate_paths_switch_name[40]; + u8 alternate_paths_switch_port[8]; + __le16 power_on_hours; + __le16 percent_endurance_used; + u8 drive_authentication; + u8 smart_carrier_authentication; + u8 smart_carrier_app_fw_version; + u8 smart_carrier_bootloader_fw_version; + u8 encryption_key_name[64]; + __le32 misc_drive_flags; + __le16 dek_index; + u8 padding[112]; +}; + +#pragma pack() + +int pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info); +void pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info); +int pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node, + struct pqi_scsi_dev *device); +void pqi_remove_sas_device(struct pqi_scsi_dev *device); +struct pqi_scsi_dev *pqi_find_device_by_sas_rphy( + struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy); + +extern struct sas_function_template pqi_sas_transport_functions; + +#if !defined(readq) +#define readq readq +static inline u64 readq(const volatile void __iomem *addr) +{ + u32 lower32; + u32 upper32; + + lower32 = readl(addr); + upper32 = readl(addr + 4); + + return ((u64)upper32 << 32) | lower32; +} +#endif + +#if !defined(writeq) +#define writeq writeq +static inline void writeq(u64 value, volatile void __iomem *addr) +{ + u32 lower32; + u32 upper32; + + lower32 = lower_32_bits(value); + upper32 = upper_32_bits(value); + + writel(lower32, addr); + writel(upper32, addr + 4); +} +#endif + +#endif /* _SMARTPQI_H */ diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c new file mode 100644 index 000000000000..906f1aaf7cd2 --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -0,0 +1,6306 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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; version 2 of the License. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smartpqi.h" +#include "smartpqi_sis.h" + +#if !defined(BUILD_TIMESTAMP) +#define BUILD_TIMESTAMP +#endif + +#define DRIVER_VERSION "0.9.9-100" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 9 +#define DRIVER_RELEASE 9 +#define DRIVER_REVISION 100 + +#define DRIVER_NAME "Microsemi PQI Driver (v" DRIVER_VERSION ")" +#define DRIVER_NAME_SHORT "smartpqi" + +MODULE_AUTHOR("Microsemi"); +MODULE_DESCRIPTION("Driver for Microsemi Smart Family Controller version " + DRIVER_VERSION); +MODULE_SUPPORTED_DEVICE("Microsemi Smart Family Controllers"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +#define PQI_ENABLE_MULTI_QUEUE_SUPPORT 0 + +static char *hpe_branded_controller = "HPE Smart Array Controller"; +static char *microsemi_branded_controller = "Microsemi Smart Family Controller"; + +static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info); +static int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info); +static void pqi_scan_start(struct Scsi_Host *shost); +static void pqi_start_io(struct pqi_ctrl_info *ctrl_info, + struct pqi_queue_group *queue_group, enum pqi_io_path path, + struct pqi_io_request *io_request); +static int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info, + struct pqi_iu_header *request, unsigned int flags, + struct pqi_raid_error_info *error_info, unsigned long timeout_msecs); +static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, + struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb, + unsigned int cdb_length, struct pqi_queue_group *queue_group, + struct pqi_encryption_info *encryption_info); + +/* for flags argument to pqi_submit_raid_request_synchronous() */ +#define PQI_SYNC_FLAGS_INTERRUPTABLE 0x1 + +static struct scsi_transport_template *pqi_sas_transport_template; + +static atomic_t pqi_controller_count = ATOMIC_INIT(0); + +static int pqi_disable_device_id_wildcards; +module_param_named(disable_device_id_wildcards, + pqi_disable_device_id_wildcards, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable_device_id_wildcards, + "Disable device ID wildcards."); + +static char *raid_levels[] = { + "RAID-0", + "RAID-4", + "RAID-1(1+0)", + "RAID-5", + "RAID-5+1", + "RAID-ADG", + "RAID-1(ADM)", +}; + +static char *pqi_raid_level_to_string(u8 raid_level) +{ + if (raid_level < ARRAY_SIZE(raid_levels)) + return raid_levels[raid_level]; + + return ""; +} + +#define SA_RAID_0 0 +#define SA_RAID_4 1 +#define SA_RAID_1 2 /* also used for RAID 10 */ +#define SA_RAID_5 3 /* also used for RAID 50 */ +#define SA_RAID_51 4 +#define SA_RAID_6 5 /* also used for RAID 60 */ +#define SA_RAID_ADM 6 /* also used for RAID 1+0 ADM */ +#define SA_RAID_MAX SA_RAID_ADM +#define SA_RAID_UNKNOWN 0xff + +static inline void pqi_scsi_done(struct scsi_cmnd *scmd) +{ + scmd->scsi_done(scmd); +} + +static inline bool pqi_scsi3addr_equal(u8 *scsi3addr1, u8 *scsi3addr2) +{ + return memcmp(scsi3addr1, scsi3addr2, 8) == 0; +} + +static inline struct pqi_ctrl_info *shost_to_hba(struct Scsi_Host *shost) +{ + void *hostdata = shost_priv(shost); + + return *((struct pqi_ctrl_info **)hostdata); +} + +static inline bool pqi_is_logical_device(struct pqi_scsi_dev *device) +{ + return !device->is_physical_device; +} + +static inline bool pqi_ctrl_offline(struct pqi_ctrl_info *ctrl_info) +{ + return !ctrl_info->controller_online; +} + +static inline void pqi_check_ctrl_health(struct pqi_ctrl_info *ctrl_info) +{ + if (ctrl_info->controller_online) + if (!sis_is_firmware_running(ctrl_info)) + pqi_take_ctrl_offline(ctrl_info); +} + +static inline bool pqi_is_hba_lunid(u8 *scsi3addr) +{ + return pqi_scsi3addr_equal(scsi3addr, RAID_CTLR_LUNID); +} + +#define PQI_RESCAN_WORK_INTERVAL (10 * HZ) + +static inline void pqi_schedule_rescan_worker(struct pqi_ctrl_info *ctrl_info) +{ + schedule_delayed_work(&ctrl_info->rescan_work, + PQI_RESCAN_WORK_INTERVAL); +} + +static int pqi_map_single(struct pci_dev *pci_dev, + struct pqi_sg_descriptor *sg_descriptor, void *buffer, + size_t buffer_length, int data_direction) +{ + dma_addr_t bus_address; + + if (!buffer || buffer_length == 0 || data_direction == PCI_DMA_NONE) + return 0; + + bus_address = pci_map_single(pci_dev, buffer, buffer_length, + data_direction); + if (pci_dma_mapping_error(pci_dev, bus_address)) + return -ENOMEM; + + put_unaligned_le64((u64)bus_address, &sg_descriptor->address); + put_unaligned_le32(buffer_length, &sg_descriptor->length); + put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags); + + return 0; +} + +static void pqi_pci_unmap(struct pci_dev *pci_dev, + struct pqi_sg_descriptor *descriptors, int num_descriptors, + int data_direction) +{ + int i; + + if (data_direction == PCI_DMA_NONE) + return; + + for (i = 0; i < num_descriptors; i++) + pci_unmap_single(pci_dev, + (dma_addr_t)get_unaligned_le64(&descriptors[i].address), + get_unaligned_le32(&descriptors[i].length), + data_direction); +} + +static int pqi_build_raid_path_request(struct pqi_ctrl_info *ctrl_info, + struct pqi_raid_path_request *request, u8 cmd, + u8 *scsi3addr, void *buffer, size_t buffer_length, + u16 vpd_page, int *pci_direction) +{ + u8 *cdb; + int pci_dir; + + memset(request, 0, sizeof(*request)); + + request->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO; + put_unaligned_le16(offsetof(struct pqi_raid_path_request, + sg_descriptors[1]) - PQI_REQUEST_HEADER_LENGTH, + &request->header.iu_length); + put_unaligned_le32(buffer_length, &request->buffer_length); + memcpy(request->lun_number, scsi3addr, sizeof(request->lun_number)); + request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + request->additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0; + + cdb = request->cdb; + + switch (cmd) { + case INQUIRY: + request->data_direction = SOP_READ_FLAG; + cdb[0] = INQUIRY; + if (vpd_page & VPD_PAGE) { + cdb[1] = 0x1; + cdb[2] = (u8)vpd_page; + } + cdb[4] = (u8)buffer_length; + break; + case CISS_REPORT_LOG: + case CISS_REPORT_PHYS: + request->data_direction = SOP_READ_FLAG; + cdb[0] = cmd; + if (cmd == CISS_REPORT_PHYS) + cdb[1] = CISS_REPORT_PHYS_EXTENDED; + else + cdb[1] = CISS_REPORT_LOG_EXTENDED; + put_unaligned_be32(buffer_length, &cdb[6]); + break; + case CISS_GET_RAID_MAP: + request->data_direction = SOP_READ_FLAG; + cdb[0] = CISS_READ; + cdb[1] = CISS_GET_RAID_MAP; + put_unaligned_be32(buffer_length, &cdb[6]); + break; + case SA_CACHE_FLUSH: + request->data_direction = SOP_WRITE_FLAG; + cdb[0] = BMIC_WRITE; + cdb[6] = BMIC_CACHE_FLUSH; + put_unaligned_be16(buffer_length, &cdb[7]); + break; + case BMIC_IDENTIFY_CONTROLLER: + case BMIC_IDENTIFY_PHYSICAL_DEVICE: + request->data_direction = SOP_READ_FLAG; + cdb[0] = BMIC_READ; + cdb[6] = cmd; + put_unaligned_be16(buffer_length, &cdb[7]); + break; + case BMIC_WRITE_HOST_WELLNESS: + request->data_direction = SOP_WRITE_FLAG; + cdb[0] = BMIC_WRITE; + cdb[6] = cmd; + put_unaligned_be16(buffer_length, &cdb[7]); + break; + default: + dev_err(&ctrl_info->pci_dev->dev, "unknown command 0x%c\n", + cmd); + WARN_ON(cmd); + break; + } + + switch (request->data_direction) { + case SOP_READ_FLAG: + pci_dir = PCI_DMA_FROMDEVICE; + break; + case SOP_WRITE_FLAG: + pci_dir = PCI_DMA_TODEVICE; + break; + case SOP_NO_DIRECTION_FLAG: + pci_dir = PCI_DMA_NONE; + break; + default: + pci_dir = PCI_DMA_BIDIRECTIONAL; + break; + } + + *pci_direction = pci_dir; + + return pqi_map_single(ctrl_info->pci_dev, &request->sg_descriptors[0], + buffer, buffer_length, pci_dir); +} + +static struct pqi_io_request *pqi_alloc_io_request( + struct pqi_ctrl_info *ctrl_info) +{ + struct pqi_io_request *io_request; + u16 i = ctrl_info->next_io_request_slot; /* benignly racy */ + + while (1) { + io_request = &ctrl_info->io_request_pool[i]; + if (atomic_inc_return(&io_request->refcount) == 1) + break; + atomic_dec(&io_request->refcount); + i = (i + 1) % ctrl_info->max_io_slots; + } + + /* benignly racy */ + ctrl_info->next_io_request_slot = (i + 1) % ctrl_info->max_io_slots; + + io_request->scmd = NULL; + io_request->status = 0; + io_request->error_info = NULL; + + return io_request; +} + +static void pqi_free_io_request(struct pqi_io_request *io_request) +{ + atomic_dec(&io_request->refcount); +} + +static int pqi_identify_controller(struct pqi_ctrl_info *ctrl_info, + struct bmic_identify_controller *buffer) +{ + int rc; + int pci_direction; + struct pqi_raid_path_request request; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + BMIC_IDENTIFY_CONTROLLER, RAID_CTLR_LUNID, buffer, + sizeof(*buffer), 0, &pci_direction); + if (rc) + return rc; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +static int pqi_scsi_inquiry(struct pqi_ctrl_info *ctrl_info, + u8 *scsi3addr, u16 vpd_page, void *buffer, size_t buffer_length) +{ + int rc; + int pci_direction; + struct pqi_raid_path_request request; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + INQUIRY, scsi3addr, buffer, buffer_length, vpd_page, + &pci_direction); + if (rc) + return rc; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +static int pqi_identify_physical_device(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, + struct bmic_identify_physical_device *buffer, + size_t buffer_length) +{ + int rc; + int pci_direction; + u16 bmic_device_index; + struct pqi_raid_path_request request; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + BMIC_IDENTIFY_PHYSICAL_DEVICE, RAID_CTLR_LUNID, buffer, + buffer_length, 0, &pci_direction); + if (rc) + return rc; + + bmic_device_index = CISS_GET_DRIVE_NUMBER(device->scsi3addr); + request.cdb[2] = (u8)bmic_device_index; + request.cdb[9] = (u8)(bmic_device_index >> 8); + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + 0, NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +#define SA_CACHE_FLUSH_BUFFER_LENGTH 4 +#define PQI_FLUSH_CACHE_TIMEOUT (30 * 1000) + +static int pqi_flush_cache(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct pqi_raid_path_request request; + int pci_direction; + u8 *buffer; + + /* + * Don't bother trying to flush the cache if the controller is + * locked up. + */ + if (pqi_ctrl_offline(ctrl_info)) + return -ENXIO; + + buffer = kzalloc(SA_CACHE_FLUSH_BUFFER_LENGTH, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + SA_CACHE_FLUSH, RAID_CTLR_LUNID, buffer, + SA_CACHE_FLUSH_BUFFER_LENGTH, 0, &pci_direction); + if (rc) + goto out; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + 0, NULL, PQI_FLUSH_CACHE_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + +out: + kfree(buffer); + + return rc; +} + +static int pqi_write_host_wellness(struct pqi_ctrl_info *ctrl_info, + void *buffer, size_t buffer_length) +{ + int rc; + struct pqi_raid_path_request request; + int pci_direction; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + BMIC_WRITE_HOST_WELLNESS, RAID_CTLR_LUNID, buffer, + buffer_length, 0, &pci_direction); + if (rc) + return rc; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + 0, NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +#pragma pack(1) + +struct bmic_host_wellness_driver_version { + u8 start_tag[4]; + u8 driver_version_tag[2]; + __le16 driver_version_length; + char driver_version[32]; + u8 end_tag[2]; +}; + +#pragma pack() + +static int pqi_write_driver_version_to_host_wellness( + struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct bmic_host_wellness_driver_version *buffer; + size_t buffer_length; + + buffer_length = sizeof(*buffer); + + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer->start_tag[0] = '<'; + buffer->start_tag[1] = 'H'; + buffer->start_tag[2] = 'W'; + buffer->start_tag[3] = '>'; + buffer->driver_version_tag[0] = 'D'; + buffer->driver_version_tag[1] = 'V'; + put_unaligned_le16(sizeof(buffer->driver_version), + &buffer->driver_version_length); + strncpy(buffer->driver_version, DRIVER_VERSION, + sizeof(buffer->driver_version) - 1); + buffer->driver_version[sizeof(buffer->driver_version) - 1] = '\0'; + buffer->end_tag[0] = 'Z'; + buffer->end_tag[1] = 'Z'; + + rc = pqi_write_host_wellness(ctrl_info, buffer, buffer_length); + + kfree(buffer); + + return rc; +} + +#pragma pack(1) + +struct bmic_host_wellness_time { + u8 start_tag[4]; + u8 time_tag[2]; + __le16 time_length; + u8 time[8]; + u8 dont_write_tag[2]; + u8 end_tag[2]; +}; + +#pragma pack() + +static int pqi_write_current_time_to_host_wellness( + struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct bmic_host_wellness_time *buffer; + size_t buffer_length; + time64_t local_time; + unsigned int year; + struct timeval time; + struct rtc_time tm; + + buffer_length = sizeof(*buffer); + + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer->start_tag[0] = '<'; + buffer->start_tag[1] = 'H'; + buffer->start_tag[2] = 'W'; + buffer->start_tag[3] = '>'; + buffer->time_tag[0] = 'T'; + buffer->time_tag[1] = 'D'; + put_unaligned_le16(sizeof(buffer->time), + &buffer->time_length); + + do_gettimeofday(&time); + local_time = time.tv_sec - (sys_tz.tz_minuteswest * 60); + rtc_time64_to_tm(local_time, &tm); + year = tm.tm_year + 1900; + + buffer->time[0] = bin2bcd(tm.tm_hour); + buffer->time[1] = bin2bcd(tm.tm_min); + buffer->time[2] = bin2bcd(tm.tm_sec); + buffer->time[3] = 0; + buffer->time[4] = bin2bcd(tm.tm_mon + 1); + buffer->time[5] = bin2bcd(tm.tm_mday); + buffer->time[6] = bin2bcd(year / 100); + buffer->time[7] = bin2bcd(year % 100); + + buffer->dont_write_tag[0] = 'D'; + buffer->dont_write_tag[1] = 'W'; + buffer->end_tag[0] = 'Z'; + buffer->end_tag[1] = 'Z'; + + rc = pqi_write_host_wellness(ctrl_info, buffer, buffer_length); + + kfree(buffer); + + return rc; +} + +#define PQI_UPDATE_TIME_WORK_INTERVAL (24UL * 60 * 60 * HZ) + +static void pqi_update_time_worker(struct work_struct *work) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info, + update_time_work); + + if (!ctrl_info) { + printk("%s: NULL controller pointer.\n", __func__); + return; + } + rc = pqi_write_current_time_to_host_wellness(ctrl_info); + if (rc) + dev_warn(&ctrl_info->pci_dev->dev, + "error updating time on controller\n"); + + schedule_delayed_work(&ctrl_info->update_time_work, + PQI_UPDATE_TIME_WORK_INTERVAL); +} + +static inline void pqi_schedule_update_time_worker( + struct pqi_ctrl_info *ctrl_info) +{ + schedule_delayed_work(&ctrl_info->update_time_work, 120); +} + +static int pqi_report_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd, + void *buffer, size_t buffer_length) +{ + int rc; + int pci_direction; + struct pqi_raid_path_request request; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + cmd, RAID_CTLR_LUNID, buffer, buffer_length, 0, &pci_direction); + if (rc) + return rc; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +static int pqi_report_phys_logical_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd, + void **buffer) +{ + int rc; + size_t lun_list_length; + size_t lun_data_length; + size_t new_lun_list_length; + void *lun_data = NULL; + struct report_lun_header *report_lun_header; + + report_lun_header = kmalloc(sizeof(*report_lun_header), GFP_KERNEL); + if (!report_lun_header) { + rc = -ENOMEM; + goto out; + } + + rc = pqi_report_luns(ctrl_info, cmd, report_lun_header, + sizeof(*report_lun_header)); + if (rc) + goto out; + + lun_list_length = get_unaligned_be32(&report_lun_header->list_length); + +again: + lun_data_length = sizeof(struct report_lun_header) + lun_list_length; + + lun_data = kmalloc(lun_data_length, GFP_KERNEL); + if (!lun_data) { + rc = -ENOMEM; + goto out; + } + + if (lun_list_length == 0) { + memcpy(lun_data, report_lun_header, sizeof(*report_lun_header)); + goto out; + } + + rc = pqi_report_luns(ctrl_info, cmd, lun_data, lun_data_length); + if (rc) + goto out; + + new_lun_list_length = get_unaligned_be32( + &((struct report_lun_header *)lun_data)->list_length); + + if (new_lun_list_length > lun_list_length) { + lun_list_length = new_lun_list_length; + kfree(lun_data); + goto again; + } + +out: + kfree(report_lun_header); + + if (rc) { + kfree(lun_data); + lun_data = NULL; + } + + *buffer = lun_data; + + return rc; +} + +static inline int pqi_report_phys_luns(struct pqi_ctrl_info *ctrl_info, + void **buffer) +{ + return pqi_report_phys_logical_luns(ctrl_info, CISS_REPORT_PHYS, + buffer); +} + +static inline int pqi_report_logical_luns(struct pqi_ctrl_info *ctrl_info, + void **buffer) +{ + return pqi_report_phys_logical_luns(ctrl_info, CISS_REPORT_LOG, buffer); +} + +static int pqi_get_device_lists(struct pqi_ctrl_info *ctrl_info, + struct report_phys_lun_extended **physdev_list, + struct report_log_lun_extended **logdev_list) +{ + int rc; + size_t logdev_list_length; + size_t logdev_data_length; + struct report_log_lun_extended *internal_logdev_list; + struct report_log_lun_extended *logdev_data; + struct report_lun_header report_lun_header; + + rc = pqi_report_phys_luns(ctrl_info, (void **)physdev_list); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "report physical LUNs failed\n"); + + rc = pqi_report_logical_luns(ctrl_info, (void **)logdev_list); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "report logical LUNs failed\n"); + + /* + * Tack the controller itself onto the end of the logical device list. + */ + + logdev_data = *logdev_list; + + if (logdev_data) { + logdev_list_length = + get_unaligned_be32(&logdev_data->header.list_length); + } else { + memset(&report_lun_header, 0, sizeof(report_lun_header)); + logdev_data = + (struct report_log_lun_extended *)&report_lun_header; + logdev_list_length = 0; + } + + logdev_data_length = sizeof(struct report_lun_header) + + logdev_list_length; + + internal_logdev_list = kmalloc(logdev_data_length + + sizeof(struct report_log_lun_extended), GFP_KERNEL); + if (!internal_logdev_list) { + kfree(*logdev_list); + *logdev_list = NULL; + return -ENOMEM; + } + + memcpy(internal_logdev_list, logdev_data, logdev_data_length); + memset((u8 *)internal_logdev_list + logdev_data_length, 0, + sizeof(struct report_log_lun_extended_entry)); + put_unaligned_be32(logdev_list_length + + sizeof(struct report_log_lun_extended_entry), + &internal_logdev_list->header.list_length); + + kfree(*logdev_list); + *logdev_list = internal_logdev_list; + + return 0; +} + +static inline void pqi_set_bus_target_lun(struct pqi_scsi_dev *device, + int bus, int target, int lun) +{ + device->bus = bus; + device->target = target; + device->lun = lun; +} + +static void pqi_assign_bus_target_lun(struct pqi_scsi_dev *device) +{ + u8 *scsi3addr; + u32 lunid; + + scsi3addr = device->scsi3addr; + lunid = get_unaligned_le32(scsi3addr); + + if (pqi_is_hba_lunid(scsi3addr)) { + /* The specified device is the controller. */ + pqi_set_bus_target_lun(device, PQI_HBA_BUS, 0, lunid & 0x3fff); + device->target_lun_valid = true; + return; + } + + if (pqi_is_logical_device(device)) { + pqi_set_bus_target_lun(device, PQI_RAID_VOLUME_BUS, 0, + lunid & 0x3fff); + device->target_lun_valid = true; + return; + } + + /* + * Defer target and LUN assignment for non-controller physical devices + * because the SAS transport layer will make these assignments later. + */ + pqi_set_bus_target_lun(device, PQI_PHYSICAL_DEVICE_BUS, 0, 0); +} + +static void pqi_get_raid_level(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + u8 raid_level; + u8 *buffer; + + raid_level = SA_RAID_UNKNOWN; + + buffer = kmalloc(64, GFP_KERNEL); + if (buffer) { + rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, + VPD_PAGE | CISS_VPD_LV_DEVICE_GEOMETRY, buffer, 64); + if (rc == 0) { + raid_level = buffer[8]; + if (raid_level > SA_RAID_MAX) + raid_level = SA_RAID_UNKNOWN; + } + kfree(buffer); + } + + device->raid_level = raid_level; +} + +static int pqi_validate_raid_map(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct raid_map *raid_map) +{ + char *err_msg; + u32 raid_map_size; + u32 r5or6_blocks_per_row; + unsigned int num_phys_disks; + unsigned int num_raid_map_entries; + + raid_map_size = get_unaligned_le32(&raid_map->structure_size); + + if (raid_map_size < offsetof(struct raid_map, disk_data)) { + err_msg = "RAID map too small"; + goto bad_raid_map; + } + + if (raid_map_size > sizeof(*raid_map)) { + err_msg = "RAID map too large"; + goto bad_raid_map; + } + + num_phys_disks = get_unaligned_le16(&raid_map->layout_map_count) * + (get_unaligned_le16(&raid_map->data_disks_per_row) + + get_unaligned_le16(&raid_map->metadata_disks_per_row)); + num_raid_map_entries = num_phys_disks * + get_unaligned_le16(&raid_map->row_cnt); + + if (num_raid_map_entries > RAID_MAP_MAX_ENTRIES) { + err_msg = "invalid number of map entries in RAID map"; + goto bad_raid_map; + } + + if (device->raid_level == SA_RAID_1) { + if (get_unaligned_le16(&raid_map->layout_map_count) != 2) { + err_msg = "invalid RAID-1 map"; + goto bad_raid_map; + } + } else if (device->raid_level == SA_RAID_ADM) { + if (get_unaligned_le16(&raid_map->layout_map_count) != 3) { + err_msg = "invalid RAID-1(ADM) map"; + goto bad_raid_map; + } + } else if ((device->raid_level == SA_RAID_5 || + device->raid_level == SA_RAID_6) && + get_unaligned_le16(&raid_map->layout_map_count) > 1) { + /* RAID 50/60 */ + r5or6_blocks_per_row = + get_unaligned_le16(&raid_map->strip_size) * + get_unaligned_le16(&raid_map->data_disks_per_row); + if (r5or6_blocks_per_row == 0) { + err_msg = "invalid RAID-5 or RAID-6 map"; + goto bad_raid_map; + } + } + + return 0; + +bad_raid_map: + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", err_msg); + + return -EINVAL; +} + +static int pqi_get_raid_map(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + int pci_direction; + struct pqi_raid_path_request request; + struct raid_map *raid_map; + + raid_map = kmalloc(sizeof(*raid_map), GFP_KERNEL); + if (!raid_map) + return -ENOMEM; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + CISS_GET_RAID_MAP, device->scsi3addr, raid_map, + sizeof(*raid_map), 0, &pci_direction); + if (rc) + goto error; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + if (rc) + goto error; + + rc = pqi_validate_raid_map(ctrl_info, device, raid_map); + if (rc) + goto error; + + device->raid_map = raid_map; + + return 0; + +error: + kfree(raid_map); + + return rc; +} + +static void pqi_get_offload_status(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + u8 *buffer; + u8 offload_status; + + buffer = kmalloc(64, GFP_KERNEL); + if (!buffer) + return; + + rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, + VPD_PAGE | CISS_VPD_LV_OFFLOAD_STATUS, buffer, 64); + if (rc) + goto out; + +#define OFFLOAD_STATUS_BYTE 4 +#define OFFLOAD_CONFIGURED_BIT 0x1 +#define OFFLOAD_ENABLED_BIT 0x2 + + offload_status = buffer[OFFLOAD_STATUS_BYTE]; + device->offload_configured = + !!(offload_status & OFFLOAD_CONFIGURED_BIT); + if (device->offload_configured) { + device->offload_enabled_pending = + !!(offload_status & OFFLOAD_ENABLED_BIT); + if (pqi_get_raid_map(ctrl_info, device)) + device->offload_enabled_pending = false; + } + +out: + kfree(buffer); +} + +/* + * Use vendor-specific VPD to determine online/offline status of a volume. + */ + +static void pqi_get_volume_status(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + size_t page_length; + u8 volume_status = CISS_LV_STATUS_UNAVAILABLE; + bool volume_offline = true; + u32 volume_flags; + struct ciss_vpd_logical_volume_status *vpd; + + vpd = kmalloc(sizeof(*vpd), GFP_KERNEL); + if (!vpd) + goto no_buffer; + + rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, + VPD_PAGE | CISS_VPD_LV_STATUS, vpd, sizeof(*vpd)); + if (rc) + goto out; + + page_length = offsetof(struct ciss_vpd_logical_volume_status, + volume_status) + vpd->page_length; + if (page_length < sizeof(*vpd)) + goto out; + + volume_status = vpd->volume_status; + volume_flags = get_unaligned_be32(&vpd->flags); + volume_offline = (volume_flags & CISS_LV_FLAGS_NO_HOST_IO) != 0; + +out: + kfree(vpd); +no_buffer: + device->volume_status = volume_status; + device->volume_offline = volume_offline; +} + +static int pqi_get_device_info(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + u8 *buffer; + + buffer = kmalloc(64, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* Send an inquiry to the device to see what it is. */ + rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, 0, buffer, 64); + if (rc) + goto out; + + scsi_sanitize_inquiry_string(&buffer[8], 8); + scsi_sanitize_inquiry_string(&buffer[16], 16); + + device->devtype = buffer[0] & 0x1f; + memcpy(device->vendor, &buffer[8], + sizeof(device->vendor)); + memcpy(device->model, &buffer[16], + sizeof(device->model)); + + if (pqi_is_logical_device(device) && device->devtype == TYPE_DISK) { + pqi_get_raid_level(ctrl_info, device); + pqi_get_offload_status(ctrl_info, device); + pqi_get_volume_status(ctrl_info, device); + } + +out: + kfree(buffer); + + return rc; +} + +static void pqi_get_physical_disk_info(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, + struct bmic_identify_physical_device *id_phys) +{ + int rc; + + memset(id_phys, 0, sizeof(*id_phys)); + + rc = pqi_identify_physical_device(ctrl_info, device, + id_phys, sizeof(*id_phys)); + if (rc) { + device->queue_depth = PQI_PHYSICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH; + return; + } + + device->queue_depth = + get_unaligned_le16(&id_phys->current_queue_depth_limit); + device->device_type = id_phys->device_type; + device->active_path_index = id_phys->active_path_number; + device->path_map = id_phys->redundant_path_present_map; + memcpy(&device->box, + &id_phys->alternate_paths_phys_box_on_port, + sizeof(device->box)); + memcpy(&device->phys_connector, + &id_phys->alternate_paths_phys_connector, + sizeof(device->phys_connector)); + device->bay = id_phys->phys_bay_in_box; +} + +static void pqi_show_volume_status(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + char *status; + static const char unknown_state_str[] = + "Volume is in an unknown state (%u)"; + char unknown_state_buffer[sizeof(unknown_state_str) + 10]; + + switch (device->volume_status) { + case CISS_LV_OK: + status = "Volume online"; + break; + case CISS_LV_FAILED: + status = "Volume failed"; + break; + case CISS_LV_NOT_CONFIGURED: + status = "Volume not configured"; + break; + case CISS_LV_DEGRADED: + status = "Volume degraded"; + break; + case CISS_LV_READY_FOR_RECOVERY: + status = "Volume ready for recovery operation"; + break; + case CISS_LV_UNDERGOING_RECOVERY: + status = "Volume undergoing recovery"; + break; + case CISS_LV_WRONG_PHYSICAL_DRIVE_REPLACED: + status = "Wrong physical drive was replaced"; + break; + case CISS_LV_PHYSICAL_DRIVE_CONNECTION_PROBLEM: + status = "A physical drive not properly connected"; + break; + case CISS_LV_HARDWARE_OVERHEATING: + status = "Hardware is overheating"; + break; + case CISS_LV_HARDWARE_HAS_OVERHEATED: + status = "Hardware has overheated"; + break; + case CISS_LV_UNDERGOING_EXPANSION: + status = "Volume undergoing expansion"; + break; + case CISS_LV_NOT_AVAILABLE: + status = "Volume waiting for transforming volume"; + break; + case CISS_LV_QUEUED_FOR_EXPANSION: + status = "Volume queued for expansion"; + break; + case CISS_LV_DISABLED_SCSI_ID_CONFLICT: + status = "Volume disabled due to SCSI ID conflict"; + break; + case CISS_LV_EJECTED: + status = "Volume has been ejected"; + break; + case CISS_LV_UNDERGOING_ERASE: + status = "Volume undergoing background erase"; + break; + case CISS_LV_READY_FOR_PREDICTIVE_SPARE_REBUILD: + status = "Volume ready for predictive spare rebuild"; + break; + case CISS_LV_UNDERGOING_RPI: + status = "Volume undergoing rapid parity initialization"; + break; + case CISS_LV_PENDING_RPI: + status = "Volume queued for rapid parity initialization"; + break; + case CISS_LV_ENCRYPTED_NO_KEY: + status = "Encrypted volume inaccessible - key not present"; + break; + case CISS_LV_UNDERGOING_ENCRYPTION: + status = "Volume undergoing encryption process"; + break; + case CISS_LV_UNDERGOING_ENCRYPTION_REKEYING: + status = "Volume undergoing encryption re-keying process"; + break; + case CISS_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER: + status = + "Encrypted volume inaccessible - disabled on ctrl"; + break; + case CISS_LV_PENDING_ENCRYPTION: + status = "Volume pending migration to encrypted state"; + break; + case CISS_LV_PENDING_ENCRYPTION_REKEYING: + status = "Volume pending encryption rekeying"; + break; + case CISS_LV_NOT_SUPPORTED: + status = "Volume not supported on this controller"; + break; + case CISS_LV_STATUS_UNAVAILABLE: + status = "Volume status not available"; + break; + default: + snprintf(unknown_state_buffer, sizeof(unknown_state_buffer), + unknown_state_str, device->volume_status); + status = unknown_state_buffer; + break; + } + + dev_info(&ctrl_info->pci_dev->dev, + "scsi %d:%d:%d:%d %s\n", + ctrl_info->scsi_host->host_no, + device->bus, device->target, device->lun, status); +} + +static struct pqi_scsi_dev *pqi_find_disk_by_aio_handle( + struct pqi_ctrl_info *ctrl_info, u32 aio_handle) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->devtype != TYPE_DISK && device->devtype != TYPE_ZBC) + continue; + if (pqi_is_logical_device(device)) + continue; + if (device->aio_handle == aio_handle) + return device; + } + + return NULL; +} + +static void pqi_update_logical_drive_queue_depth( + struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *logical_drive) +{ + unsigned int i; + struct raid_map *raid_map; + struct raid_map_disk_data *disk_data; + struct pqi_scsi_dev *phys_disk; + unsigned int num_phys_disks; + unsigned int num_raid_map_entries; + unsigned int queue_depth; + + logical_drive->queue_depth = PQI_LOGICAL_DRIVE_DEFAULT_MAX_QUEUE_DEPTH; + + raid_map = logical_drive->raid_map; + if (!raid_map) + return; + + disk_data = raid_map->disk_data; + num_phys_disks = get_unaligned_le16(&raid_map->layout_map_count) * + (get_unaligned_le16(&raid_map->data_disks_per_row) + + get_unaligned_le16(&raid_map->metadata_disks_per_row)); + num_raid_map_entries = num_phys_disks * + get_unaligned_le16(&raid_map->row_cnt); + + queue_depth = 0; + for (i = 0; i < num_raid_map_entries; i++) { + phys_disk = pqi_find_disk_by_aio_handle(ctrl_info, + disk_data[i].aio_handle); + + if (!phys_disk) { + dev_warn(&ctrl_info->pci_dev->dev, + "failed to find physical disk for logical drive %016llx\n", + get_unaligned_be64(logical_drive->scsi3addr)); + logical_drive->offload_enabled = false; + logical_drive->offload_enabled_pending = false; + kfree(raid_map); + logical_drive->raid_map = NULL; + return; + } + + queue_depth += phys_disk->queue_depth; + } + + logical_drive->queue_depth = queue_depth; +} + +static void pqi_update_all_logical_drive_queue_depths( + struct pqi_ctrl_info *ctrl_info) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->devtype != TYPE_DISK && device->devtype != TYPE_ZBC) + continue; + if (!pqi_is_logical_device(device)) + continue; + pqi_update_logical_drive_queue_depth(ctrl_info, device); + } +} + +static void pqi_rescan_worker(struct work_struct *work) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info, + rescan_work); + + pqi_scan_scsi_devices(ctrl_info); +} + +static int pqi_add_device(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + + if (pqi_is_logical_device(device)) + rc = scsi_add_device(ctrl_info->scsi_host, device->bus, + device->target, device->lun); + else + rc = pqi_add_sas_device(ctrl_info->sas_host, device); + + return rc; +} + +static inline void pqi_remove_device(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + if (pqi_is_logical_device(device)) + scsi_remove_device(device->sdev); + else + pqi_remove_sas_device(device); +} + +/* Assumes the SCSI device list lock is held. */ + +static struct pqi_scsi_dev *pqi_find_scsi_dev(struct pqi_ctrl_info *ctrl_info, + int bus, int target, int lun) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) + if (device->bus == bus && device->target == target && + device->lun == lun) + return device; + + return NULL; +} + +static inline bool pqi_device_equal(struct pqi_scsi_dev *dev1, + struct pqi_scsi_dev *dev2) +{ + if (dev1->is_physical_device != dev2->is_physical_device) + return false; + + if (dev1->is_physical_device) + return dev1->wwid == dev2->wwid; + + return memcmp(dev1->volume_id, dev2->volume_id, + sizeof(dev1->volume_id)) == 0; +} + +enum pqi_find_result { + DEVICE_NOT_FOUND, + DEVICE_CHANGED, + DEVICE_SAME, +}; + +static enum pqi_find_result pqi_scsi_find_entry(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device_to_find, + struct pqi_scsi_dev **matching_device) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (pqi_scsi3addr_equal(device_to_find->scsi3addr, + device->scsi3addr)) { + *matching_device = device; + if (pqi_device_equal(device_to_find, device)) { + if (device_to_find->volume_offline) + return DEVICE_CHANGED; + return DEVICE_SAME; + } + return DEVICE_CHANGED; + } + } + + return DEVICE_NOT_FOUND; +} + +static void pqi_dev_info(struct pqi_ctrl_info *ctrl_info, + char *action, struct pqi_scsi_dev *device) +{ + dev_info(&ctrl_info->pci_dev->dev, + "%s scsi %d:%d:%d:%d: %s %.8s %.16s %-12s SSDSmartPathCap%c En%c Exp%c qd=%d\n", + action, + ctrl_info->scsi_host->host_no, + device->bus, + device->target, + device->lun, + scsi_device_type(device->devtype), + device->vendor, + device->model, + pqi_raid_level_to_string(device->raid_level), + device->offload_configured ? '+' : '-', + device->offload_enabled_pending ? '+' : '-', + device->expose_device ? '+' : '-', + device->queue_depth); +} + +/* Assumes the SCSI device list lock is held. */ + +static void pqi_scsi_update_device(struct pqi_scsi_dev *existing_device, + struct pqi_scsi_dev *new_device) +{ + existing_device->devtype = new_device->devtype; + existing_device->device_type = new_device->device_type; + existing_device->bus = new_device->bus; + if (new_device->target_lun_valid) { + existing_device->target = new_device->target; + existing_device->lun = new_device->lun; + existing_device->target_lun_valid = true; + } + + /* By definition, the scsi3addr and wwid fields are already the same. */ + + existing_device->is_physical_device = new_device->is_physical_device; + existing_device->expose_device = new_device->expose_device; + existing_device->no_uld_attach = new_device->no_uld_attach; + existing_device->aio_enabled = new_device->aio_enabled; + memcpy(existing_device->vendor, new_device->vendor, + sizeof(existing_device->vendor)); + memcpy(existing_device->model, new_device->model, + sizeof(existing_device->model)); + existing_device->sas_address = new_device->sas_address; + existing_device->raid_level = new_device->raid_level; + existing_device->queue_depth = new_device->queue_depth; + existing_device->aio_handle = new_device->aio_handle; + existing_device->volume_status = new_device->volume_status; + existing_device->active_path_index = new_device->active_path_index; + existing_device->path_map = new_device->path_map; + existing_device->bay = new_device->bay; + memcpy(existing_device->box, new_device->box, + sizeof(existing_device->box)); + memcpy(existing_device->phys_connector, new_device->phys_connector, + sizeof(existing_device->phys_connector)); + existing_device->offload_configured = new_device->offload_configured; + existing_device->offload_enabled = false; + existing_device->offload_enabled_pending = + new_device->offload_enabled_pending; + existing_device->offload_to_mirror = 0; + kfree(existing_device->raid_map); + existing_device->raid_map = new_device->raid_map; + + /* To prevent this from being freed later. */ + new_device->raid_map = NULL; +} + +static inline void pqi_free_device(struct pqi_scsi_dev *device) +{ + if (device) { + kfree(device->raid_map); + kfree(device); + } +} + +/* + * Called when exposing a new device to the OS fails in order to re-adjust + * our internal SCSI device list to match the SCSI ML's view. + */ + +static inline void pqi_fixup_botched_add(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + unsigned long flags; + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + list_del(&device->scsi_device_list_entry); + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + /* Allow the device structure to be freed later. */ + device->keep_device = false; +} + +static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *new_device_list[], unsigned int num_new_devices) +{ + int rc; + unsigned int i; + unsigned long flags; + enum pqi_find_result find_result; + struct pqi_scsi_dev *device; + struct pqi_scsi_dev *next; + struct pqi_scsi_dev *matching_device; + struct list_head add_list; + struct list_head delete_list; + + INIT_LIST_HEAD(&add_list); + INIT_LIST_HEAD(&delete_list); + + /* + * The idea here is to do as little work as possible while holding the + * spinlock. That's why we go to great pains to defer anything other + * than updating the internal device list until after we release the + * spinlock. + */ + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + /* Assume that all devices in the existing list have gone away. */ + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) + device->device_gone = true; + + for (i = 0; i < num_new_devices; i++) { + device = new_device_list[i]; + + find_result = pqi_scsi_find_entry(ctrl_info, device, + &matching_device); + + switch (find_result) { + case DEVICE_SAME: + /* + * The newly found device is already in the existing + * device list. + */ + device->new_device = false; + matching_device->device_gone = false; + pqi_scsi_update_device(matching_device, device); + break; + case DEVICE_NOT_FOUND: + /* + * The newly found device is NOT in the existing device + * list. + */ + device->new_device = true; + break; + case DEVICE_CHANGED: + /* + * The original device has gone away and we need to add + * the new device. + */ + device->new_device = true; + break; + default: + WARN_ON(find_result); + break; + } + } + + /* Process all devices that have gone away. */ + list_for_each_entry_safe(device, next, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->device_gone) { + list_del(&device->scsi_device_list_entry); + list_add_tail(&device->delete_list_entry, &delete_list); + } + } + + /* Process all new devices. */ + for (i = 0; i < num_new_devices; i++) { + device = new_device_list[i]; + if (!device->new_device) + continue; + if (device->volume_offline) + continue; + list_add_tail(&device->scsi_device_list_entry, + &ctrl_info->scsi_device_list); + list_add_tail(&device->add_list_entry, &add_list); + /* To prevent this device structure from being freed later. */ + device->keep_device = true; + } + + pqi_update_all_logical_drive_queue_depths(ctrl_info); + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) + device->offload_enabled = + device->offload_enabled_pending; + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + /* Remove all devices that have gone away. */ + list_for_each_entry_safe(device, next, &delete_list, + delete_list_entry) { + if (device->sdev) + pqi_remove_device(ctrl_info, device); + if (device->volume_offline) { + pqi_dev_info(ctrl_info, "offline", device); + pqi_show_volume_status(ctrl_info, device); + } else { + pqi_dev_info(ctrl_info, "removed", device); + } + list_del(&device->delete_list_entry); + pqi_free_device(device); + } + + /* + * Notify the SCSI ML if the queue depth of any existing device has + * changed. + */ + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->sdev && device->queue_depth != + device->advertised_queue_depth) { + device->advertised_queue_depth = device->queue_depth; + scsi_change_queue_depth(device->sdev, + device->advertised_queue_depth); + } + } + + /* Expose any new devices. */ + list_for_each_entry_safe(device, next, &add_list, add_list_entry) { + if (device->expose_device && !device->sdev) { + rc = pqi_add_device(ctrl_info, device); + if (rc) { + dev_warn(&ctrl_info->pci_dev->dev, + "scsi %d:%d:%d:%d addition failed, device not added\n", + ctrl_info->scsi_host->host_no, + device->bus, device->target, + device->lun); + pqi_fixup_botched_add(ctrl_info, device); + continue; + } + } + pqi_dev_info(ctrl_info, "added", device); + } +} + +static bool pqi_is_supported_device(struct pqi_scsi_dev *device) +{ + bool is_supported = false; + + switch (device->devtype) { + case TYPE_DISK: + case TYPE_ZBC: + case TYPE_TAPE: + case TYPE_MEDIUM_CHANGER: + case TYPE_ENCLOSURE: + is_supported = true; + break; + case TYPE_RAID: + /* + * Only support the HBA controller itself as a RAID + * controller. If it's a RAID controller other than + * the HBA itself (an external RAID controller, MSA500 + * or similar), we don't support it. + */ + if (pqi_is_hba_lunid(device->scsi3addr)) + is_supported = true; + break; + } + + return is_supported; +} + +static inline bool pqi_skip_device(u8 *scsi3addr, + struct report_phys_lun_extended_entry *phys_lun_ext_entry) +{ + u8 device_flags; + + if (!MASKED_DEVICE(scsi3addr)) + return false; + + /* The device is masked. */ + + device_flags = phys_lun_ext_entry->device_flags; + + if (device_flags & REPORT_PHYS_LUN_DEV_FLAG_NON_DISK) { + /* + * It's a non-disk device. We ignore all devices of this type + * when they're masked. + */ + return true; + } + + return false; +} + +static inline bool pqi_expose_device(struct pqi_scsi_dev *device) +{ + /* Expose all devices except for physical devices that are masked. */ + if (device->is_physical_device && MASKED_DEVICE(device->scsi3addr)) + return false; + + return true; +} + +static int pqi_update_scsi_devices(struct pqi_ctrl_info *ctrl_info) +{ + int i; + int rc; + struct list_head new_device_list_head; + struct report_phys_lun_extended *physdev_list = NULL; + struct report_log_lun_extended *logdev_list = NULL; + struct report_phys_lun_extended_entry *phys_lun_ext_entry; + struct report_log_lun_extended_entry *log_lun_ext_entry; + struct bmic_identify_physical_device *id_phys = NULL; + u32 num_physicals; + u32 num_logicals; + struct pqi_scsi_dev **new_device_list = NULL; + struct pqi_scsi_dev *device; + struct pqi_scsi_dev *next; + unsigned int num_new_devices; + unsigned int num_valid_devices; + bool is_physical_device; + u8 *scsi3addr; + static char *out_of_memory_msg = + "out of memory, device discovery stopped"; + + INIT_LIST_HEAD(&new_device_list_head); + + rc = pqi_get_device_lists(ctrl_info, &physdev_list, &logdev_list); + if (rc) + goto out; + + if (physdev_list) + num_physicals = + get_unaligned_be32(&physdev_list->header.list_length) + / sizeof(physdev_list->lun_entries[0]); + else + num_physicals = 0; + + if (logdev_list) + num_logicals = + get_unaligned_be32(&logdev_list->header.list_length) + / sizeof(logdev_list->lun_entries[0]); + else + num_logicals = 0; + + if (num_physicals) { + /* + * We need this buffer for calls to pqi_get_physical_disk_info() + * below. We allocate it here instead of inside + * pqi_get_physical_disk_info() because it's a fairly large + * buffer. + */ + id_phys = kmalloc(sizeof(*id_phys), GFP_KERNEL); + if (!id_phys) { + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", + out_of_memory_msg); + rc = -ENOMEM; + goto out; + } + } + + num_new_devices = num_physicals + num_logicals; + + new_device_list = kmalloc(sizeof(*new_device_list) * + num_new_devices, GFP_KERNEL); + if (!new_device_list) { + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", out_of_memory_msg); + rc = -ENOMEM; + goto out; + } + + for (i = 0; i < num_new_devices; i++) { + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) { + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", + out_of_memory_msg); + rc = -ENOMEM; + goto out; + } + list_add_tail(&device->new_device_list_entry, + &new_device_list_head); + } + + device = NULL; + num_valid_devices = 0; + + for (i = 0; i < num_new_devices; i++) { + + if (i < num_physicals) { + is_physical_device = true; + phys_lun_ext_entry = &physdev_list->lun_entries[i]; + log_lun_ext_entry = NULL; + scsi3addr = phys_lun_ext_entry->lunid; + } else { + is_physical_device = false; + phys_lun_ext_entry = NULL; + log_lun_ext_entry = + &logdev_list->lun_entries[i - num_physicals]; + scsi3addr = log_lun_ext_entry->lunid; + } + + if (is_physical_device && + pqi_skip_device(scsi3addr, phys_lun_ext_entry)) + continue; + + if (device) + device = list_next_entry(device, new_device_list_entry); + else + device = list_first_entry(&new_device_list_head, + struct pqi_scsi_dev, new_device_list_entry); + + memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr)); + device->is_physical_device = is_physical_device; + device->raid_level = SA_RAID_UNKNOWN; + + /* Gather information about the device. */ + rc = pqi_get_device_info(ctrl_info, device); + if (rc == -ENOMEM) { + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", + out_of_memory_msg); + goto out; + } + if (rc) { + dev_warn(&ctrl_info->pci_dev->dev, + "obtaining device info failed, skipping device %016llx\n", + get_unaligned_be64(device->scsi3addr)); + rc = 0; + continue; + } + + if (!pqi_is_supported_device(device)) + continue; + + pqi_assign_bus_target_lun(device); + + device->expose_device = pqi_expose_device(device); + + if (device->is_physical_device) { + device->wwid = phys_lun_ext_entry->wwid; + if ((phys_lun_ext_entry->device_flags & + REPORT_PHYS_LUN_DEV_FLAG_AIO_ENABLED) && + phys_lun_ext_entry->aio_handle) + device->aio_enabled = true; + } else { + memcpy(device->volume_id, log_lun_ext_entry->volume_id, + sizeof(device->volume_id)); + } + + switch (device->devtype) { + case TYPE_DISK: + case TYPE_ZBC: + case TYPE_ENCLOSURE: + if (device->is_physical_device) { + device->sas_address = + get_unaligned_be64(&device->wwid); + if (device->devtype == TYPE_DISK || + device->devtype == TYPE_ZBC) { + device->aio_handle = + phys_lun_ext_entry->aio_handle; + pqi_get_physical_disk_info(ctrl_info, + device, id_phys); + } + } + break; + } + + new_device_list[num_valid_devices++] = device; + } + + pqi_update_device_list(ctrl_info, new_device_list, num_valid_devices); + +out: + list_for_each_entry_safe(device, next, &new_device_list_head, + new_device_list_entry) { + if (device->keep_device) + continue; + list_del(&device->new_device_list_entry); + pqi_free_device(device); + } + + kfree(new_device_list); + kfree(physdev_list); + kfree(logdev_list); + kfree(id_phys); + + return rc; +} + +static void pqi_remove_all_scsi_devices(struct pqi_ctrl_info *ctrl_info) +{ + unsigned long flags; + struct pqi_scsi_dev *device; + struct pqi_scsi_dev *next; + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + list_for_each_entry_safe(device, next, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->sdev) + pqi_remove_device(ctrl_info, device); + list_del(&device->scsi_device_list_entry); + pqi_free_device(device); + } + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); +} + +static int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + + if (pqi_ctrl_offline(ctrl_info)) + return -ENXIO; + + mutex_lock(&ctrl_info->scan_mutex); + + rc = pqi_update_scsi_devices(ctrl_info); + if (rc) + pqi_schedule_rescan_worker(ctrl_info); + + mutex_unlock(&ctrl_info->scan_mutex); + + return rc; +} + +static void pqi_scan_start(struct Scsi_Host *shost) +{ + pqi_scan_scsi_devices(shost_to_hba(shost)); +} + +/* Returns TRUE if scan is finished. */ + +static int pqi_scan_finished(struct Scsi_Host *shost, + unsigned long elapsed_time) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = shost_priv(shost); + + return !mutex_is_locked(&ctrl_info->scan_mutex); +} + +static inline void pqi_set_encryption_info( + struct pqi_encryption_info *encryption_info, struct raid_map *raid_map, + u64 first_block) +{ + u32 volume_blk_size; + + /* + * Set the encryption tweak values based on logical block address. + * If the block size is 512, the tweak value is equal to the LBA. + * For other block sizes, tweak value is (LBA * block size) / 512. + */ + volume_blk_size = get_unaligned_le32(&raid_map->volume_blk_size); + if (volume_blk_size != 512) + first_block = (first_block * volume_blk_size) / 512; + + encryption_info->data_encryption_key_index = + get_unaligned_le16(&raid_map->data_encryption_key_index); + encryption_info->encrypt_tweak_lower = lower_32_bits(first_block); + encryption_info->encrypt_tweak_upper = upper_32_bits(first_block); +} + +/* + * Attempt to perform offload RAID mapping for a logical volume I/O. + */ + +#define PQI_RAID_BYPASS_INELIGIBLE 1 + +static int pqi_raid_bypass_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, + struct pqi_queue_group *queue_group) +{ + struct raid_map *raid_map; + bool is_write = false; + u32 map_index; + u64 first_block; + u64 last_block; + u32 block_cnt; + u32 blocks_per_row; + u64 first_row; + u64 last_row; + u32 first_row_offset; + u32 last_row_offset; + u32 first_column; + u32 last_column; + u64 r0_first_row; + u64 r0_last_row; + u32 r5or6_blocks_per_row; + u64 r5or6_first_row; + u64 r5or6_last_row; + u32 r5or6_first_row_offset; + u32 r5or6_last_row_offset; + u32 r5or6_first_column; + u32 r5or6_last_column; + u16 data_disks_per_row; + u32 total_disks_per_row; + u16 layout_map_count; + u32 stripesize; + u16 strip_size; + u32 first_group; + u32 last_group; + u32 current_group; + u32 map_row; + u32 aio_handle; + u64 disk_block; + u32 disk_block_cnt; + u8 cdb[16]; + u8 cdb_length; + int offload_to_mirror; + struct pqi_encryption_info *encryption_info_ptr; + struct pqi_encryption_info encryption_info; +#if BITS_PER_LONG == 32 + u64 tmpdiv; +#endif + + /* Check for valid opcode, get LBA and block count. */ + switch (scmd->cmnd[0]) { + case WRITE_6: + is_write = true; + /* fall through */ + case READ_6: + first_block = (u64)get_unaligned_be16(&scmd->cmnd[2]); + block_cnt = (u32)scmd->cmnd[4]; + if (block_cnt == 0) + block_cnt = 256; + break; + case WRITE_10: + is_write = true; + /* fall through */ + case READ_10: + first_block = (u64)get_unaligned_be32(&scmd->cmnd[2]); + block_cnt = (u32)get_unaligned_be16(&scmd->cmnd[7]); + break; + case WRITE_12: + is_write = true; + /* fall through */ + case READ_12: + first_block = (u64)get_unaligned_be32(&scmd->cmnd[2]); + block_cnt = get_unaligned_be32(&scmd->cmnd[6]); + break; + case WRITE_16: + is_write = true; + /* fall through */ + case READ_16: + first_block = get_unaligned_be64(&scmd->cmnd[2]); + block_cnt = get_unaligned_be32(&scmd->cmnd[10]); + break; + default: + /* Process via normal I/O path. */ + return PQI_RAID_BYPASS_INELIGIBLE; + } + + /* Check for write to non-RAID-0. */ + if (is_write && device->raid_level != SA_RAID_0) + return PQI_RAID_BYPASS_INELIGIBLE; + + if (unlikely(block_cnt == 0)) + return PQI_RAID_BYPASS_INELIGIBLE; + + last_block = first_block + block_cnt - 1; + raid_map = device->raid_map; + + /* Check for invalid block or wraparound. */ + if (last_block >= get_unaligned_le64(&raid_map->volume_blk_cnt) || + last_block < first_block) + return PQI_RAID_BYPASS_INELIGIBLE; + + data_disks_per_row = get_unaligned_le16(&raid_map->data_disks_per_row); + strip_size = get_unaligned_le16(&raid_map->strip_size); + layout_map_count = get_unaligned_le16(&raid_map->layout_map_count); + + /* Calculate stripe information for the request. */ + blocks_per_row = data_disks_per_row * strip_size; +#if BITS_PER_LONG == 32 + tmpdiv = first_block; + do_div(tmpdiv, blocks_per_row); + first_row = tmpdiv; + tmpdiv = last_block; + do_div(tmpdiv, blocks_per_row); + last_row = tmpdiv; + first_row_offset = (u32)(first_block - (first_row * blocks_per_row)); + last_row_offset = (u32)(last_block - (last_row * blocks_per_row)); + tmpdiv = first_row_offset; + do_div(tmpdiv, strip_size); + first_column = tmpdiv; + tmpdiv = last_row_offset; + do_div(tmpdiv, strip_size); + last_column = tmpdiv; +#else + first_row = first_block / blocks_per_row; + last_row = last_block / blocks_per_row; + first_row_offset = (u32)(first_block - (first_row * blocks_per_row)); + last_row_offset = (u32)(last_block - (last_row * blocks_per_row)); + first_column = first_row_offset / strip_size; + last_column = last_row_offset / strip_size; +#endif + + /* If this isn't a single row/column then give to the controller. */ + if (first_row != last_row || first_column != last_column) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Proceeding with driver mapping. */ + total_disks_per_row = data_disks_per_row + + get_unaligned_le16(&raid_map->metadata_disks_per_row); + map_row = ((u32)(first_row >> raid_map->parity_rotation_shift)) % + get_unaligned_le16(&raid_map->row_cnt); + map_index = (map_row * total_disks_per_row) + first_column; + + /* RAID 1 */ + if (device->raid_level == SA_RAID_1) { + if (device->offload_to_mirror) + map_index += data_disks_per_row; + device->offload_to_mirror = !device->offload_to_mirror; + } else if (device->raid_level == SA_RAID_ADM) { + /* RAID ADM */ + /* + * Handles N-way mirrors (R1-ADM) and R10 with # of drives + * divisible by 3. + */ + offload_to_mirror = device->offload_to_mirror; + if (offload_to_mirror == 0) { + /* use physical disk in the first mirrored group. */ + map_index %= data_disks_per_row; + } else { + do { + /* + * Determine mirror group that map_index + * indicates. + */ + current_group = map_index / data_disks_per_row; + + if (offload_to_mirror != current_group) { + if (current_group < + layout_map_count - 1) { + /* + * Select raid index from + * next group. + */ + map_index += data_disks_per_row; + current_group++; + } else { + /* + * Select raid index from first + * group. + */ + map_index %= data_disks_per_row; + current_group = 0; + } + } + } while (offload_to_mirror != current_group); + } + + /* Set mirror group to use next time. */ + offload_to_mirror = + (offload_to_mirror >= layout_map_count - 1) ? + 0 : offload_to_mirror + 1; + WARN_ON(offload_to_mirror >= layout_map_count); + device->offload_to_mirror = offload_to_mirror; + /* + * Avoid direct use of device->offload_to_mirror within this + * function since multiple threads might simultaneously + * increment it beyond the range of device->layout_map_count -1. + */ + } else if ((device->raid_level == SA_RAID_5 || + device->raid_level == SA_RAID_6) && layout_map_count > 1) { + /* RAID 50/60 */ + /* Verify first and last block are in same RAID group */ + r5or6_blocks_per_row = strip_size * data_disks_per_row; + stripesize = r5or6_blocks_per_row * layout_map_count; +#if BITS_PER_LONG == 32 + tmpdiv = first_block; + first_group = do_div(tmpdiv, stripesize); + tmpdiv = first_group; + do_div(tmpdiv, r5or6_blocks_per_row); + first_group = tmpdiv; + tmpdiv = last_block; + last_group = do_div(tmpdiv, stripesize); + tmpdiv = last_group; + do_div(tmpdiv, r5or6_blocks_per_row); + last_group = tmpdiv; +#else + first_group = (first_block % stripesize) / r5or6_blocks_per_row; + last_group = (last_block % stripesize) / r5or6_blocks_per_row; +#endif + if (first_group != last_group) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Verify request is in a single row of RAID 5/6 */ +#if BITS_PER_LONG == 32 + tmpdiv = first_block; + do_div(tmpdiv, stripesize); + first_row = r5or6_first_row = r0_first_row = tmpdiv; + tmpdiv = last_block; + do_div(tmpdiv, stripesize); + r5or6_last_row = r0_last_row = tmpdiv; +#else + first_row = r5or6_first_row = r0_first_row = + first_block / stripesize; + r5or6_last_row = r0_last_row = last_block / stripesize; +#endif + if (r5or6_first_row != r5or6_last_row) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Verify request is in a single column */ +#if BITS_PER_LONG == 32 + tmpdiv = first_block; + first_row_offset = do_div(tmpdiv, stripesize); + tmpdiv = first_row_offset; + first_row_offset = (u32)do_div(tmpdiv, r5or6_blocks_per_row); + r5or6_first_row_offset = first_row_offset; + tmpdiv = last_block; + r5or6_last_row_offset = do_div(tmpdiv, stripesize); + tmpdiv = r5or6_last_row_offset; + r5or6_last_row_offset = do_div(tmpdiv, r5or6_blocks_per_row); + tmpdiv = r5or6_first_row_offset; + do_div(tmpdiv, strip_size); + first_column = r5or6_first_column = tmpdiv; + tmpdiv = r5or6_last_row_offset; + do_div(tmpdiv, strip_size); + r5or6_last_column = tmpdiv; +#else + first_row_offset = r5or6_first_row_offset = + (u32)((first_block % stripesize) % + r5or6_blocks_per_row); + + r5or6_last_row_offset = + (u32)((last_block % stripesize) % + r5or6_blocks_per_row); + + first_column = r5or6_first_row_offset / strip_size; + r5or6_first_column = first_column; + r5or6_last_column = r5or6_last_row_offset / strip_size; +#endif + if (r5or6_first_column != r5or6_last_column) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Request is eligible */ + map_row = + ((u32)(first_row >> raid_map->parity_rotation_shift)) % + get_unaligned_le16(&raid_map->row_cnt); + + map_index = (first_group * + (get_unaligned_le16(&raid_map->row_cnt) * + total_disks_per_row)) + + (map_row * total_disks_per_row) + first_column; + } + + if (unlikely(map_index >= RAID_MAP_MAX_ENTRIES)) + return PQI_RAID_BYPASS_INELIGIBLE; + + aio_handle = raid_map->disk_data[map_index].aio_handle; + disk_block = get_unaligned_le64(&raid_map->disk_starting_blk) + + first_row * strip_size + + (first_row_offset - first_column * strip_size); + disk_block_cnt = block_cnt; + + /* Handle differing logical/physical block sizes. */ + if (raid_map->phys_blk_shift) { + disk_block <<= raid_map->phys_blk_shift; + disk_block_cnt <<= raid_map->phys_blk_shift; + } + + if (unlikely(disk_block_cnt > 0xffff)) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Build the new CDB for the physical disk I/O. */ + if (disk_block > 0xffffffff) { + cdb[0] = is_write ? WRITE_16 : READ_16; + cdb[1] = 0; + put_unaligned_be64(disk_block, &cdb[2]); + put_unaligned_be32(disk_block_cnt, &cdb[10]); + cdb[14] = 0; + cdb[15] = 0; + cdb_length = 16; + } else { + cdb[0] = is_write ? WRITE_10 : READ_10; + cdb[1] = 0; + put_unaligned_be32((u32)disk_block, &cdb[2]); + cdb[6] = 0; + put_unaligned_be16((u16)disk_block_cnt, &cdb[7]); + cdb[9] = 0; + cdb_length = 10; + } + + if (get_unaligned_le16(&raid_map->flags) & + RAID_MAP_ENCRYPTION_ENABLED) { + pqi_set_encryption_info(&encryption_info, raid_map, + first_block); + encryption_info_ptr = &encryption_info; + } else { + encryption_info_ptr = NULL; + } + + return pqi_aio_submit_io(ctrl_info, scmd, aio_handle, + cdb, cdb_length, queue_group, encryption_info_ptr); +} + +#define PQI_STATUS_IDLE 0x0 + +#define PQI_CREATE_ADMIN_QUEUE_PAIR 1 +#define PQI_DELETE_ADMIN_QUEUE_PAIR 2 + +#define PQI_DEVICE_STATE_POWER_ON_AND_RESET 0x0 +#define PQI_DEVICE_STATE_STATUS_AVAILABLE 0x1 +#define PQI_DEVICE_STATE_ALL_REGISTERS_READY 0x2 +#define PQI_DEVICE_STATE_ADMIN_QUEUE_PAIR_READY 0x3 +#define PQI_DEVICE_STATE_ERROR 0x4 + +#define PQI_MODE_READY_TIMEOUT_SECS 30 +#define PQI_MODE_READY_POLL_INTERVAL_MSECS 1 + +static int pqi_wait_for_pqi_mode_ready(struct pqi_ctrl_info *ctrl_info) +{ + struct pqi_device_registers __iomem *pqi_registers; + unsigned long timeout; + u64 signature; + u8 status; + + pqi_registers = ctrl_info->pqi_registers; + timeout = (PQI_MODE_READY_TIMEOUT_SECS * HZ) + jiffies; + + while (1) { + signature = readq(&pqi_registers->signature); + if (memcmp(&signature, PQI_DEVICE_SIGNATURE, + sizeof(signature)) == 0) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "timed out waiting for PQI signature\n"); + return -ETIMEDOUT; + } + msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); + } + + while (1) { + status = readb(&pqi_registers->function_and_status_code); + if (status == PQI_STATUS_IDLE) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "timed out waiting for PQI IDLE\n"); + return -ETIMEDOUT; + } + msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); + } + + while (1) { + if (readl(&pqi_registers->device_status) == + PQI_DEVICE_STATE_ALL_REGISTERS_READY) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "timed out waiting for PQI all registers ready\n"); + return -ETIMEDOUT; + } + msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); + } + + return 0; +} + +static inline void pqi_aio_path_disabled(struct pqi_io_request *io_request) +{ + struct pqi_scsi_dev *device; + + device = io_request->scmd->device->hostdata; + device->offload_enabled = false; +} + +static inline void pqi_take_device_offline(struct scsi_device *sdev) +{ + struct pqi_ctrl_info *ctrl_info; + + if (scsi_device_online(sdev)) { + scsi_device_set_state(sdev, SDEV_OFFLINE); + ctrl_info = shost_to_hba(sdev->host); + schedule_delayed_work(&ctrl_info->rescan_work, 0); + } +} + +static void pqi_process_raid_io_error(struct pqi_io_request *io_request) +{ + u8 scsi_status; + u8 host_byte; + struct scsi_cmnd *scmd; + struct pqi_raid_error_info *error_info; + size_t sense_data_length; + int residual_count; + int xfer_count; + struct scsi_sense_hdr sshdr; + + scmd = io_request->scmd; + if (!scmd) + return; + + error_info = io_request->error_info; + scsi_status = error_info->status; + host_byte = DID_OK; + + if (error_info->data_out_result == PQI_DATA_IN_OUT_UNDERFLOW) { + xfer_count = + get_unaligned_le32(&error_info->data_out_transferred); + residual_count = scsi_bufflen(scmd) - xfer_count; + scsi_set_resid(scmd, residual_count); + if (xfer_count < scmd->underflow) + host_byte = DID_SOFT_ERROR; + } + + sense_data_length = get_unaligned_le16(&error_info->sense_data_length); + if (sense_data_length == 0) + sense_data_length = + get_unaligned_le16(&error_info->response_data_length); + if (sense_data_length) { + if (sense_data_length > sizeof(error_info->data)) + sense_data_length = sizeof(error_info->data); + + if (scsi_status == SAM_STAT_CHECK_CONDITION && + scsi_normalize_sense(error_info->data, + sense_data_length, &sshdr) && + sshdr.sense_key == HARDWARE_ERROR && + sshdr.asc == 0x3e && + sshdr.ascq == 0x1) { + pqi_take_device_offline(scmd->device); + host_byte = DID_NO_CONNECT; + } + + if (sense_data_length > SCSI_SENSE_BUFFERSIZE) + sense_data_length = SCSI_SENSE_BUFFERSIZE; + memcpy(scmd->sense_buffer, error_info->data, + sense_data_length); + } + + scmd->result = scsi_status; + set_host_byte(scmd, host_byte); +} + +static void pqi_process_aio_io_error(struct pqi_io_request *io_request) +{ + u8 scsi_status; + u8 host_byte; + struct scsi_cmnd *scmd; + struct pqi_aio_error_info *error_info; + size_t sense_data_length; + int residual_count; + int xfer_count; + bool device_offline; + + scmd = io_request->scmd; + error_info = io_request->error_info; + host_byte = DID_OK; + sense_data_length = 0; + device_offline = false; + + switch (error_info->service_response) { + case PQI_AIO_SERV_RESPONSE_COMPLETE: + scsi_status = error_info->status; + break; + case PQI_AIO_SERV_RESPONSE_FAILURE: + switch (error_info->status) { + case PQI_AIO_STATUS_IO_ABORTED: + scsi_status = SAM_STAT_TASK_ABORTED; + break; + case PQI_AIO_STATUS_UNDERRUN: + scsi_status = SAM_STAT_GOOD; + residual_count = get_unaligned_le32( + &error_info->residual_count); + scsi_set_resid(scmd, residual_count); + xfer_count = scsi_bufflen(scmd) - residual_count; + if (xfer_count < scmd->underflow) + host_byte = DID_SOFT_ERROR; + break; + case PQI_AIO_STATUS_OVERRUN: + scsi_status = SAM_STAT_GOOD; + break; + case PQI_AIO_STATUS_AIO_PATH_DISABLED: + pqi_aio_path_disabled(io_request); + scsi_status = SAM_STAT_GOOD; + io_request->status = -EAGAIN; + break; + case PQI_AIO_STATUS_NO_PATH_TO_DEVICE: + case PQI_AIO_STATUS_INVALID_DEVICE: + device_offline = true; + pqi_take_device_offline(scmd->device); + host_byte = DID_NO_CONNECT; + scsi_status = SAM_STAT_CHECK_CONDITION; + break; + case PQI_AIO_STATUS_IO_ERROR: + default: + scsi_status = SAM_STAT_CHECK_CONDITION; + break; + } + break; + case PQI_AIO_SERV_RESPONSE_TMF_COMPLETE: + case PQI_AIO_SERV_RESPONSE_TMF_SUCCEEDED: + scsi_status = SAM_STAT_GOOD; + break; + case PQI_AIO_SERV_RESPONSE_TMF_REJECTED: + case PQI_AIO_SERV_RESPONSE_TMF_INCORRECT_LUN: + default: + scsi_status = SAM_STAT_CHECK_CONDITION; + break; + } + + if (error_info->data_present) { + sense_data_length = + get_unaligned_le16(&error_info->data_length); + if (sense_data_length) { + if (sense_data_length > sizeof(error_info->data)) + sense_data_length = sizeof(error_info->data); + if (sense_data_length > SCSI_SENSE_BUFFERSIZE) + sense_data_length = SCSI_SENSE_BUFFERSIZE; + memcpy(scmd->sense_buffer, error_info->data, + sense_data_length); + } + } + + if (device_offline && sense_data_length == 0) + scsi_build_sense_buffer(0, scmd->sense_buffer, HARDWARE_ERROR, + 0x3e, 0x1); + + scmd->result = scsi_status; + set_host_byte(scmd, host_byte); +} + +static void pqi_process_io_error(unsigned int iu_type, + struct pqi_io_request *io_request) +{ + switch (iu_type) { + case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: + pqi_process_raid_io_error(io_request); + break; + case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: + pqi_process_aio_io_error(io_request); + break; + } +} + +static int pqi_interpret_task_management_response( + struct pqi_task_management_response *response) +{ + int rc; + + switch (response->response_code) { + case SOP_TASK_MANAGEMENT_FUNCTION_COMPLETE: + case SOP_TASK_MANAGEMENT_FUNCTION_SUCCEEDED: + rc = 0; + break; + default: + rc = -EIO; + break; + } + + return rc; +} + +static unsigned int pqi_process_io_intr(struct pqi_ctrl_info *ctrl_info, + struct pqi_queue_group *queue_group) +{ + unsigned int num_responses; + pqi_index_t oq_pi; + pqi_index_t oq_ci; + struct pqi_io_request *io_request; + struct pqi_io_response *response; + u16 request_id; + + num_responses = 0; + oq_ci = queue_group->oq_ci_copy; + + while (1) { + oq_pi = *queue_group->oq_pi; + if (oq_pi == oq_ci) + break; + + num_responses++; + response = queue_group->oq_element_array + + (oq_ci * PQI_OPERATIONAL_OQ_ELEMENT_LENGTH); + + request_id = get_unaligned_le16(&response->request_id); + WARN_ON(request_id >= ctrl_info->max_io_slots); + + io_request = &ctrl_info->io_request_pool[request_id]; + WARN_ON(atomic_read(&io_request->refcount) == 0); + + switch (response->header.iu_type) { + case PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS: + case PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS: + case PQI_RESPONSE_IU_GENERAL_MANAGEMENT: + break; + case PQI_RESPONSE_IU_TASK_MANAGEMENT: + io_request->status = + pqi_interpret_task_management_response( + (void *)response); + break; + case PQI_RESPONSE_IU_AIO_PATH_DISABLED: + pqi_aio_path_disabled(io_request); + io_request->status = -EAGAIN; + break; + case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: + case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: + io_request->error_info = ctrl_info->error_buffer + + (get_unaligned_le16(&response->error_index) * + PQI_ERROR_BUFFER_ELEMENT_LENGTH); + pqi_process_io_error(response->header.iu_type, + io_request); + break; + default: + dev_err(&ctrl_info->pci_dev->dev, + "unexpected IU type: 0x%x\n", + response->header.iu_type); + WARN_ON(response->header.iu_type); + break; + } + + io_request->io_complete_callback(io_request, + io_request->context); + + /* + * Note that the I/O request structure CANNOT BE TOUCHED after + * returning from the I/O completion callback! + */ + + oq_ci = (oq_ci + 1) % ctrl_info->num_elements_per_oq; + } + + if (num_responses) { + queue_group->oq_ci_copy = oq_ci; + writel(oq_ci, queue_group->oq_ci); + } + + return num_responses; +} + +static inline unsigned int pqi_num_elements_free(unsigned int pi, + unsigned int ci, + unsigned int elements_in_queue) +{ + unsigned int num_elements_used; + + if (pi >= ci) + num_elements_used = pi - ci; + else + num_elements_used = elements_in_queue - ci + pi; + + return elements_in_queue - num_elements_used - 1; +} + +#define PQI_EVENT_ACK_TIMEOUT 30 + +static void pqi_start_event_ack(struct pqi_ctrl_info *ctrl_info, + struct pqi_event_acknowledge_request *iu, size_t iu_length) +{ + pqi_index_t iq_pi; + pqi_index_t iq_ci; + unsigned long flags; + void *next_element; + unsigned long timeout; + struct pqi_queue_group *queue_group; + + queue_group = &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP]; + put_unaligned_le16(queue_group->oq_id, &iu->header.response_queue_id); + + timeout = (PQI_EVENT_ACK_TIMEOUT * HZ) + jiffies; + + while (1) { + spin_lock_irqsave(&queue_group->submit_lock[RAID_PATH], flags); + + iq_pi = queue_group->iq_pi_copy[RAID_PATH]; + iq_ci = *queue_group->iq_ci[RAID_PATH]; + + if (pqi_num_elements_free(iq_pi, iq_ci, + ctrl_info->num_elements_per_iq)) + break; + + spin_unlock_irqrestore( + &queue_group->submit_lock[RAID_PATH], flags); + + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "sending event acknowledge timed out\n"); + return; + } + } + + next_element = queue_group->iq_element_array[RAID_PATH] + + (iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + memcpy(next_element, iu, iu_length); + + iq_pi = (iq_pi + 1) % ctrl_info->num_elements_per_iq; + + queue_group->iq_pi_copy[RAID_PATH] = iq_pi; + + /* + * This write notifies the controller that an IU is available to be + * processed. + */ + writel(iq_pi, queue_group->iq_pi[RAID_PATH]); + + spin_unlock_irqrestore(&queue_group->submit_lock[RAID_PATH], flags); + + /* + * We have to special-case this type of request because the firmware + * does not generate an interrupt when this type of request completes. + * Therefore, we have to poll until we see that the firmware has + * consumed the request before we move on. + */ + + timeout = (PQI_EVENT_ACK_TIMEOUT * HZ) + jiffies; + + while (1) { + if (*queue_group->iq_ci[RAID_PATH] == iq_pi) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "completing event acknowledge timed out\n"); + break; + } + usleep_range(1000, 2000); + } +} + +static void pqi_acknowledge_event(struct pqi_ctrl_info *ctrl_info, + struct pqi_event *event) +{ + struct pqi_event_acknowledge_request request; + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_ACKNOWLEDGE_VENDOR_EVENT; + put_unaligned_le16(sizeof(request) - PQI_REQUEST_HEADER_LENGTH, + &request.header.iu_length); + request.event_type = event->event_type; + request.event_id = event->event_id; + request.additional_event_id = event->additional_event_id; + + pqi_start_event_ack(ctrl_info, &request, sizeof(request)); +} + +static void pqi_event_worker(struct work_struct *work) +{ + unsigned int i; + struct pqi_ctrl_info *ctrl_info; + struct pqi_event *pending_event; + bool got_non_heartbeat_event = false; + + ctrl_info = container_of(work, struct pqi_ctrl_info, event_work); + + pending_event = ctrl_info->pending_events; + for (i = 0; i < PQI_NUM_SUPPORTED_EVENTS; i++) { + if (pending_event->pending) { + pending_event->pending = false; + pqi_acknowledge_event(ctrl_info, pending_event); + if (i != PQI_EVENT_HEARTBEAT) + got_non_heartbeat_event = true; + } + pending_event++; + } + + if (got_non_heartbeat_event) + pqi_schedule_rescan_worker(ctrl_info); +} + +static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + unsigned int path; + struct pqi_queue_group *queue_group; + unsigned long flags; + struct pqi_io_request *io_request; + struct pqi_io_request *next; + struct scsi_cmnd *scmd; + + ctrl_info->controller_online = false; + dev_err(&ctrl_info->pci_dev->dev, "controller offline\n"); + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + + for (path = 0; path < 2; path++) { + spin_lock_irqsave( + &queue_group->submit_lock[path], flags); + + list_for_each_entry_safe(io_request, next, + &queue_group->request_list[path], + request_list_entry) { + + scmd = io_request->scmd; + if (scmd) { + set_host_byte(scmd, DID_NO_CONNECT); + pqi_scsi_done(scmd); + } + + list_del(&io_request->request_list_entry); + } + + spin_unlock_irqrestore( + &queue_group->submit_lock[path], flags); + } + } +} + +#define PQI_HEARTBEAT_TIMER_INTERVAL (5 * HZ) +#define PQI_MAX_HEARTBEAT_REQUESTS 5 + +static void pqi_heartbeat_timer_handler(unsigned long data) +{ + int num_interrupts; + struct pqi_ctrl_info *ctrl_info = (struct pqi_ctrl_info *)data; + + num_interrupts = atomic_read(&ctrl_info->num_interrupts); + + if (num_interrupts == ctrl_info->previous_num_interrupts) { + ctrl_info->num_heartbeats_requested++; + if (ctrl_info->num_heartbeats_requested > + PQI_MAX_HEARTBEAT_REQUESTS) { + pqi_take_ctrl_offline(ctrl_info); + return; + } + ctrl_info->pending_events[PQI_EVENT_HEARTBEAT].pending = true; + schedule_work(&ctrl_info->event_work); + } else { + ctrl_info->num_heartbeats_requested = 0; + } + + ctrl_info->previous_num_interrupts = num_interrupts; + mod_timer(&ctrl_info->heartbeat_timer, + jiffies + PQI_HEARTBEAT_TIMER_INTERVAL); +} + +static void pqi_start_heartbeat_timer(struct pqi_ctrl_info *ctrl_info) +{ + ctrl_info->previous_num_interrupts = + atomic_read(&ctrl_info->num_interrupts); + + init_timer(&ctrl_info->heartbeat_timer); + ctrl_info->heartbeat_timer.expires = + jiffies + PQI_HEARTBEAT_TIMER_INTERVAL; + ctrl_info->heartbeat_timer.data = (unsigned long)ctrl_info; + ctrl_info->heartbeat_timer.function = pqi_heartbeat_timer_handler; + add_timer(&ctrl_info->heartbeat_timer); + ctrl_info->heartbeat_timer_started = true; +} + +static inline void pqi_stop_heartbeat_timer(struct pqi_ctrl_info *ctrl_info) +{ + if (ctrl_info->heartbeat_timer_started) + del_timer_sync(&ctrl_info->heartbeat_timer); +} + +static int pqi_event_type_to_event_index(unsigned int event_type) +{ + int index; + + switch (event_type) { + case PQI_EVENT_TYPE_HEARTBEAT: + index = PQI_EVENT_HEARTBEAT; + break; + case PQI_EVENT_TYPE_HOTPLUG: + index = PQI_EVENT_HOTPLUG; + break; + case PQI_EVENT_TYPE_HARDWARE: + index = PQI_EVENT_HARDWARE; + break; + case PQI_EVENT_TYPE_PHYSICAL_DEVICE: + index = PQI_EVENT_PHYSICAL_DEVICE; + break; + case PQI_EVENT_TYPE_LOGICAL_DEVICE: + index = PQI_EVENT_LOGICAL_DEVICE; + break; + case PQI_EVENT_TYPE_AIO_STATE_CHANGE: + index = PQI_EVENT_AIO_STATE_CHANGE; + break; + case PQI_EVENT_TYPE_AIO_CONFIG_CHANGE: + index = PQI_EVENT_AIO_CONFIG_CHANGE; + break; + default: + index = -1; + break; + } + + return index; +} + +static unsigned int pqi_process_event_intr(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int num_events; + pqi_index_t oq_pi; + pqi_index_t oq_ci; + struct pqi_event_queue *event_queue; + struct pqi_event_response *response; + struct pqi_event *pending_event; + bool need_delayed_work; + int event_index; + + event_queue = &ctrl_info->event_queue; + num_events = 0; + need_delayed_work = false; + oq_ci = event_queue->oq_ci_copy; + + while (1) { + oq_pi = *event_queue->oq_pi; + if (oq_pi == oq_ci) + break; + + num_events++; + response = event_queue->oq_element_array + + (oq_ci * PQI_EVENT_OQ_ELEMENT_LENGTH); + + event_index = + pqi_event_type_to_event_index(response->event_type); + + if (event_index >= 0) { + if (response->request_acknowlege) { + pending_event = + &ctrl_info->pending_events[event_index]; + pending_event->event_type = + response->event_type; + pending_event->event_id = response->event_id; + pending_event->additional_event_id = + response->additional_event_id; + if (event_index != PQI_EVENT_HEARTBEAT) { + pending_event->pending = true; + need_delayed_work = true; + } + } + } + + oq_ci = (oq_ci + 1) % PQI_NUM_EVENT_QUEUE_ELEMENTS; + } + + if (num_events) { + event_queue->oq_ci_copy = oq_ci; + writel(oq_ci, event_queue->oq_ci); + + if (need_delayed_work) + schedule_work(&ctrl_info->event_work); + } + + return num_events; +} + +static irqreturn_t pqi_irq_handler(int irq, void *data) +{ + struct pqi_ctrl_info *ctrl_info; + struct pqi_queue_group *queue_group; + unsigned int num_responses_handled; + + queue_group = data; + ctrl_info = queue_group->ctrl_info; + + if (!ctrl_info || !queue_group->oq_ci) + return IRQ_NONE; + + num_responses_handled = pqi_process_io_intr(ctrl_info, queue_group); + + if (irq == ctrl_info->event_irq) + num_responses_handled += pqi_process_event_intr(ctrl_info); + + if (num_responses_handled) + atomic_inc(&ctrl_info->num_interrupts); + + pqi_start_io(ctrl_info, queue_group, RAID_PATH, NULL); + pqi_start_io(ctrl_info, queue_group, AIO_PATH, NULL); + + return IRQ_HANDLED; +} + +static int pqi_request_irqs(struct pqi_ctrl_info *ctrl_info) +{ + int i; + int rc; + + ctrl_info->event_irq = ctrl_info->msix_vectors[0]; + + for (i = 0; i < ctrl_info->num_msix_vectors_enabled; i++) { + rc = request_irq(ctrl_info->msix_vectors[i], + pqi_irq_handler, 0, + DRIVER_NAME_SHORT, ctrl_info->intr_data[i]); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "irq %u init failed with error %d\n", + ctrl_info->msix_vectors[i], rc); + return rc; + } + ctrl_info->num_msix_vectors_initialized++; + } + + return 0; +} + +static void pqi_free_irqs(struct pqi_ctrl_info *ctrl_info) +{ + int i; + + for (i = 0; i < ctrl_info->num_msix_vectors_initialized; i++) + free_irq(ctrl_info->msix_vectors[i], + ctrl_info->intr_data[i]); +} + +static int pqi_enable_msix_interrupts(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + int max_vectors; + int num_vectors_enabled; + struct msix_entry msix_entries[PQI_MAX_MSIX_VECTORS]; + + max_vectors = ctrl_info->num_queue_groups; + + for (i = 0; i < max_vectors; i++) + msix_entries[i].entry = i; + + num_vectors_enabled = pci_enable_msix_range(ctrl_info->pci_dev, + msix_entries, PQI_MIN_MSIX_VECTORS, max_vectors); + + if (num_vectors_enabled < 0) { + dev_err(&ctrl_info->pci_dev->dev, + "MSI-X init failed with error %d\n", + num_vectors_enabled); + return num_vectors_enabled; + } + + ctrl_info->num_msix_vectors_enabled = num_vectors_enabled; + for (i = 0; i < num_vectors_enabled; i++) { + ctrl_info->msix_vectors[i] = msix_entries[i].vector; + ctrl_info->intr_data[i] = &ctrl_info->queue_groups[i]; + } + + return 0; +} + +static void pqi_irq_set_affinity_hint(struct pqi_ctrl_info *ctrl_info) +{ + int i; + int rc; + int cpu; + + cpu = cpumask_first(cpu_online_mask); + for (i = 0; i < ctrl_info->num_msix_vectors_initialized; i++) { + rc = irq_set_affinity_hint(ctrl_info->msix_vectors[i], + get_cpu_mask(cpu)); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "error %d setting affinity hint for irq vector %u\n", + rc, ctrl_info->msix_vectors[i]); + cpu = cpumask_next(cpu, cpu_online_mask); + } +} + +static void pqi_irq_unset_affinity_hint(struct pqi_ctrl_info *ctrl_info) +{ + int i; + + for (i = 0; i < ctrl_info->num_msix_vectors_initialized; i++) + irq_set_affinity_hint(ctrl_info->msix_vectors[i], NULL); +} + +static int pqi_alloc_operational_queues(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + size_t alloc_length; + size_t element_array_length_per_iq; + size_t element_array_length_per_oq; + void *element_array; + void *next_queue_index; + void *aligned_pointer; + unsigned int num_inbound_queues; + unsigned int num_outbound_queues; + unsigned int num_queue_indexes; + struct pqi_queue_group *queue_group; + + element_array_length_per_iq = + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH * + ctrl_info->num_elements_per_iq; + element_array_length_per_oq = + PQI_OPERATIONAL_OQ_ELEMENT_LENGTH * + ctrl_info->num_elements_per_oq; + num_inbound_queues = ctrl_info->num_queue_groups * 2; + num_outbound_queues = ctrl_info->num_queue_groups; + num_queue_indexes = (ctrl_info->num_queue_groups * 3) + 1; + + aligned_pointer = NULL; + + for (i = 0; i < num_inbound_queues; i++) { + aligned_pointer = PTR_ALIGN(aligned_pointer, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + aligned_pointer += element_array_length_per_iq; + } + + for (i = 0; i < num_outbound_queues; i++) { + aligned_pointer = PTR_ALIGN(aligned_pointer, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + aligned_pointer += element_array_length_per_oq; + } + + aligned_pointer = PTR_ALIGN(aligned_pointer, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + aligned_pointer += PQI_NUM_EVENT_QUEUE_ELEMENTS * + PQI_EVENT_OQ_ELEMENT_LENGTH; + + for (i = 0; i < num_queue_indexes; i++) { + aligned_pointer = PTR_ALIGN(aligned_pointer, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + aligned_pointer += sizeof(pqi_index_t); + } + + alloc_length = (size_t)aligned_pointer + + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT; + + ctrl_info->queue_memory_base = + dma_zalloc_coherent(&ctrl_info->pci_dev->dev, + alloc_length, + &ctrl_info->queue_memory_base_dma_handle, GFP_KERNEL); + + if (!ctrl_info->queue_memory_base) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate memory for PQI admin queues\n"); + return -ENOMEM; + } + + ctrl_info->queue_memory_length = alloc_length; + + element_array = PTR_ALIGN(ctrl_info->queue_memory_base, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + queue_group->iq_element_array[RAID_PATH] = element_array; + queue_group->iq_element_array_bus_addr[RAID_PATH] = + ctrl_info->queue_memory_base_dma_handle + + (element_array - ctrl_info->queue_memory_base); + element_array += element_array_length_per_iq; + element_array = PTR_ALIGN(element_array, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + queue_group->iq_element_array[AIO_PATH] = element_array; + queue_group->iq_element_array_bus_addr[AIO_PATH] = + ctrl_info->queue_memory_base_dma_handle + + (element_array - ctrl_info->queue_memory_base); + element_array += element_array_length_per_iq; + element_array = PTR_ALIGN(element_array, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + } + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + queue_group->oq_element_array = element_array; + queue_group->oq_element_array_bus_addr = + ctrl_info->queue_memory_base_dma_handle + + (element_array - ctrl_info->queue_memory_base); + element_array += element_array_length_per_oq; + element_array = PTR_ALIGN(element_array, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + } + + ctrl_info->event_queue.oq_element_array = element_array; + ctrl_info->event_queue.oq_element_array_bus_addr = + ctrl_info->queue_memory_base_dma_handle + + (element_array - ctrl_info->queue_memory_base); + element_array += PQI_NUM_EVENT_QUEUE_ELEMENTS * + PQI_EVENT_OQ_ELEMENT_LENGTH; + + next_queue_index = PTR_ALIGN(element_array, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + queue_group->iq_ci[RAID_PATH] = next_queue_index; + queue_group->iq_ci_bus_addr[RAID_PATH] = + ctrl_info->queue_memory_base_dma_handle + + (next_queue_index - ctrl_info->queue_memory_base); + next_queue_index += sizeof(pqi_index_t); + next_queue_index = PTR_ALIGN(next_queue_index, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + queue_group->iq_ci[AIO_PATH] = next_queue_index; + queue_group->iq_ci_bus_addr[AIO_PATH] = + ctrl_info->queue_memory_base_dma_handle + + (next_queue_index - ctrl_info->queue_memory_base); + next_queue_index += sizeof(pqi_index_t); + next_queue_index = PTR_ALIGN(next_queue_index, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + queue_group->oq_pi = next_queue_index; + queue_group->oq_pi_bus_addr = + ctrl_info->queue_memory_base_dma_handle + + (next_queue_index - ctrl_info->queue_memory_base); + next_queue_index += sizeof(pqi_index_t); + next_queue_index = PTR_ALIGN(next_queue_index, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + } + + ctrl_info->event_queue.oq_pi = next_queue_index; + ctrl_info->event_queue.oq_pi_bus_addr = + ctrl_info->queue_memory_base_dma_handle + + (next_queue_index - ctrl_info->queue_memory_base); + + return 0; +} + +static void pqi_init_operational_queues(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + u16 next_iq_id = PQI_MIN_OPERATIONAL_QUEUE_ID; + u16 next_oq_id = PQI_MIN_OPERATIONAL_QUEUE_ID; + + /* + * Initialize the backpointers to the controller structure in + * each operational queue group structure. + */ + for (i = 0; i < ctrl_info->num_queue_groups; i++) + ctrl_info->queue_groups[i].ctrl_info = ctrl_info; + + /* + * Assign IDs to all operational queues. Note that the IDs + * assigned to operational IQs are independent of the IDs + * assigned to operational OQs. + */ + ctrl_info->event_queue.oq_id = next_oq_id++; + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + ctrl_info->queue_groups[i].iq_id[RAID_PATH] = next_iq_id++; + ctrl_info->queue_groups[i].iq_id[AIO_PATH] = next_iq_id++; + ctrl_info->queue_groups[i].oq_id = next_oq_id++; + } + + /* + * Assign MSI-X table entry indexes to all queues. Note that the + * interrupt for the event queue is shared with the first queue group. + */ + ctrl_info->event_queue.int_msg_num = 0; + for (i = 0; i < ctrl_info->num_queue_groups; i++) + ctrl_info->queue_groups[i].int_msg_num = i; + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + spin_lock_init(&ctrl_info->queue_groups[i].submit_lock[0]); + spin_lock_init(&ctrl_info->queue_groups[i].submit_lock[1]); + INIT_LIST_HEAD(&ctrl_info->queue_groups[i].request_list[0]); + INIT_LIST_HEAD(&ctrl_info->queue_groups[i].request_list[1]); + } +} + +static int pqi_alloc_admin_queues(struct pqi_ctrl_info *ctrl_info) +{ + size_t alloc_length; + struct pqi_admin_queues_aligned *admin_queues_aligned; + struct pqi_admin_queues *admin_queues; + + alloc_length = sizeof(struct pqi_admin_queues_aligned) + + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT; + + ctrl_info->admin_queue_memory_base = + dma_zalloc_coherent(&ctrl_info->pci_dev->dev, + alloc_length, + &ctrl_info->admin_queue_memory_base_dma_handle, + GFP_KERNEL); + + if (!ctrl_info->admin_queue_memory_base) + return -ENOMEM; + + ctrl_info->admin_queue_memory_length = alloc_length; + + admin_queues = &ctrl_info->admin_queues; + admin_queues_aligned = PTR_ALIGN(ctrl_info->admin_queue_memory_base, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + admin_queues->iq_element_array = + &admin_queues_aligned->iq_element_array; + admin_queues->oq_element_array = + &admin_queues_aligned->oq_element_array; + admin_queues->iq_ci = &admin_queues_aligned->iq_ci; + admin_queues->oq_pi = &admin_queues_aligned->oq_pi; + + admin_queues->iq_element_array_bus_addr = + ctrl_info->admin_queue_memory_base_dma_handle + + (admin_queues->iq_element_array - + ctrl_info->admin_queue_memory_base); + admin_queues->oq_element_array_bus_addr = + ctrl_info->admin_queue_memory_base_dma_handle + + (admin_queues->oq_element_array - + ctrl_info->admin_queue_memory_base); + admin_queues->iq_ci_bus_addr = + ctrl_info->admin_queue_memory_base_dma_handle + + ((void *)admin_queues->iq_ci - + ctrl_info->admin_queue_memory_base); + admin_queues->oq_pi_bus_addr = + ctrl_info->admin_queue_memory_base_dma_handle + + ((void *)admin_queues->oq_pi - + ctrl_info->admin_queue_memory_base); + + return 0; +} + +#define PQI_ADMIN_QUEUE_CREATE_TIMEOUT_JIFFIES HZ +#define PQI_ADMIN_QUEUE_CREATE_POLL_INTERVAL_MSECS 1 + +static int pqi_create_admin_queues(struct pqi_ctrl_info *ctrl_info) +{ + struct pqi_device_registers __iomem *pqi_registers; + struct pqi_admin_queues *admin_queues; + unsigned long timeout; + u8 status; + u32 reg; + + pqi_registers = ctrl_info->pqi_registers; + admin_queues = &ctrl_info->admin_queues; + + writeq((u64)admin_queues->iq_element_array_bus_addr, + &pqi_registers->admin_iq_element_array_addr); + writeq((u64)admin_queues->oq_element_array_bus_addr, + &pqi_registers->admin_oq_element_array_addr); + writeq((u64)admin_queues->iq_ci_bus_addr, + &pqi_registers->admin_iq_ci_addr); + writeq((u64)admin_queues->oq_pi_bus_addr, + &pqi_registers->admin_oq_pi_addr); + + reg = PQI_ADMIN_IQ_NUM_ELEMENTS | + (PQI_ADMIN_OQ_NUM_ELEMENTS) << 8 | + (admin_queues->int_msg_num << 16); + writel(reg, &pqi_registers->admin_iq_num_elements); + writel(PQI_CREATE_ADMIN_QUEUE_PAIR, + &pqi_registers->function_and_status_code); + + timeout = PQI_ADMIN_QUEUE_CREATE_TIMEOUT_JIFFIES + jiffies; + while (1) { + status = readb(&pqi_registers->function_and_status_code); + if (status == PQI_STATUS_IDLE) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + msleep(PQI_ADMIN_QUEUE_CREATE_POLL_INTERVAL_MSECS); + } + + /* + * The offset registers are not initialized to the correct + * offsets until *after* the create admin queue pair command + * completes successfully. + */ + admin_queues->iq_pi = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + readq(&pqi_registers->admin_iq_pi_offset); + admin_queues->oq_ci = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + readq(&pqi_registers->admin_oq_ci_offset); + + return 0; +} + +static void pqi_submit_admin_request(struct pqi_ctrl_info *ctrl_info, + struct pqi_general_admin_request *request) +{ + struct pqi_admin_queues *admin_queues; + void *next_element; + pqi_index_t iq_pi; + + admin_queues = &ctrl_info->admin_queues; + iq_pi = admin_queues->iq_pi_copy; + + next_element = admin_queues->iq_element_array + + (iq_pi * PQI_ADMIN_IQ_ELEMENT_LENGTH); + + memcpy(next_element, request, sizeof(*request)); + + iq_pi = (iq_pi + 1) % PQI_ADMIN_IQ_NUM_ELEMENTS; + admin_queues->iq_pi_copy = iq_pi; + + /* + * This write notifies the controller that an IU is available to be + * processed. + */ + writel(iq_pi, admin_queues->iq_pi); +} + +static int pqi_poll_for_admin_response(struct pqi_ctrl_info *ctrl_info, + struct pqi_general_admin_response *response) +{ + struct pqi_admin_queues *admin_queues; + pqi_index_t oq_pi; + pqi_index_t oq_ci; + unsigned long timeout; + + admin_queues = &ctrl_info->admin_queues; + oq_ci = admin_queues->oq_ci_copy; + + timeout = (3 * HZ) + jiffies; + + while (1) { + oq_pi = *admin_queues->oq_pi; + if (oq_pi != oq_ci) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "timed out waiting for admin response\n"); + return -ETIMEDOUT; + } + usleep_range(1000, 2000); + } + + memcpy(response, admin_queues->oq_element_array + + (oq_ci * PQI_ADMIN_OQ_ELEMENT_LENGTH), sizeof(*response)); + + oq_ci = (oq_ci + 1) % PQI_ADMIN_OQ_NUM_ELEMENTS; + admin_queues->oq_ci_copy = oq_ci; + writel(oq_ci, admin_queues->oq_ci); + + return 0; +} + +static void pqi_start_io(struct pqi_ctrl_info *ctrl_info, + struct pqi_queue_group *queue_group, enum pqi_io_path path, + struct pqi_io_request *io_request) +{ + struct pqi_io_request *next; + void *next_element; + pqi_index_t iq_pi; + pqi_index_t iq_ci; + size_t iu_length; + unsigned long flags; + unsigned int num_elements_needed; + unsigned int num_elements_to_end_of_queue; + size_t copy_count; + struct pqi_iu_header *request; + + spin_lock_irqsave(&queue_group->submit_lock[path], flags); + + if (io_request) + list_add_tail(&io_request->request_list_entry, + &queue_group->request_list[path]); + + iq_pi = queue_group->iq_pi_copy[path]; + + list_for_each_entry_safe(io_request, next, + &queue_group->request_list[path], request_list_entry) { + + request = io_request->iu; + + iu_length = get_unaligned_le16(&request->iu_length) + + PQI_REQUEST_HEADER_LENGTH; + num_elements_needed = + DIV_ROUND_UP(iu_length, + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + iq_ci = *queue_group->iq_ci[path]; + + if (num_elements_needed > pqi_num_elements_free(iq_pi, iq_ci, + ctrl_info->num_elements_per_iq)) + break; + + put_unaligned_le16(queue_group->oq_id, + &request->response_queue_id); + + next_element = queue_group->iq_element_array[path] + + (iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + num_elements_to_end_of_queue = + ctrl_info->num_elements_per_iq - iq_pi; + + if (num_elements_needed <= num_elements_to_end_of_queue) { + memcpy(next_element, request, iu_length); + } else { + copy_count = num_elements_to_end_of_queue * + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH; + memcpy(next_element, request, copy_count); + memcpy(queue_group->iq_element_array[path], + (u8 *)request + copy_count, + iu_length - copy_count); + } + + iq_pi = (iq_pi + num_elements_needed) % + ctrl_info->num_elements_per_iq; + + list_del(&io_request->request_list_entry); + } + + if (iq_pi != queue_group->iq_pi_copy[path]) { + queue_group->iq_pi_copy[path] = iq_pi; + /* + * This write notifies the controller that one or more IUs are + * available to be processed. + */ + writel(iq_pi, queue_group->iq_pi[path]); + } + + spin_unlock_irqrestore(&queue_group->submit_lock[path], flags); +} + +static void pqi_raid_synchronous_complete(struct pqi_io_request *io_request, + void *context) +{ + struct completion *waiting = context; + + complete(waiting); +} + +static int pqi_submit_raid_request_synchronous_with_io_request( + struct pqi_ctrl_info *ctrl_info, struct pqi_io_request *io_request, + unsigned long timeout_msecs) +{ + int rc = 0; + DECLARE_COMPLETION_ONSTACK(wait); + + io_request->io_complete_callback = pqi_raid_synchronous_complete; + io_request->context = &wait; + + pqi_start_io(ctrl_info, + &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH, + io_request); + + if (timeout_msecs == NO_TIMEOUT) { + wait_for_completion_io(&wait); + } else { + if (!wait_for_completion_io_timeout(&wait, + msecs_to_jiffies(timeout_msecs))) { + dev_warn(&ctrl_info->pci_dev->dev, + "command timed out\n"); + rc = -ETIMEDOUT; + } + } + + return rc; +} + +static int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info, + struct pqi_iu_header *request, unsigned int flags, + struct pqi_raid_error_info *error_info, unsigned long timeout_msecs) +{ + int rc; + struct pqi_io_request *io_request; + unsigned long start_jiffies; + unsigned long msecs_blocked; + size_t iu_length; + + /* + * Note that specifying PQI_SYNC_FLAGS_INTERRUPTABLE and a timeout value + * are mutually exclusive. + */ + + if (flags & PQI_SYNC_FLAGS_INTERRUPTABLE) { + if (down_interruptible(&ctrl_info->sync_request_sem)) + return -ERESTARTSYS; + } else { + if (timeout_msecs == NO_TIMEOUT) { + down(&ctrl_info->sync_request_sem); + } else { + start_jiffies = jiffies; + if (down_timeout(&ctrl_info->sync_request_sem, + msecs_to_jiffies(timeout_msecs))) + return -ETIMEDOUT; + msecs_blocked = + jiffies_to_msecs(jiffies - start_jiffies); + if (msecs_blocked >= timeout_msecs) + return -ETIMEDOUT; + timeout_msecs -= msecs_blocked; + } + } + + io_request = pqi_alloc_io_request(ctrl_info); + + put_unaligned_le16(io_request->index, + &(((struct pqi_raid_path_request *)request)->request_id)); + + if (request->iu_type == PQI_REQUEST_IU_RAID_PATH_IO) + ((struct pqi_raid_path_request *)request)->error_index = + ((struct pqi_raid_path_request *)request)->request_id; + + iu_length = get_unaligned_le16(&request->iu_length) + + PQI_REQUEST_HEADER_LENGTH; + memcpy(io_request->iu, request, iu_length); + + rc = pqi_submit_raid_request_synchronous_with_io_request(ctrl_info, + io_request, timeout_msecs); + + if (error_info) { + if (io_request->error_info) + memcpy(error_info, io_request->error_info, + sizeof(*error_info)); + else + memset(error_info, 0, sizeof(*error_info)); + } else if (rc == 0 && io_request->error_info) { + u8 scsi_status; + struct pqi_raid_error_info *raid_error_info; + + raid_error_info = io_request->error_info; + scsi_status = raid_error_info->status; + + if (scsi_status == SAM_STAT_CHECK_CONDITION && + raid_error_info->data_out_result == + PQI_DATA_IN_OUT_UNDERFLOW) + scsi_status = SAM_STAT_GOOD; + + if (scsi_status != SAM_STAT_GOOD) + rc = -EIO; + } + + pqi_free_io_request(io_request); + + up(&ctrl_info->sync_request_sem); + + return rc; +} + +static int pqi_validate_admin_response( + struct pqi_general_admin_response *response, u8 expected_function_code) +{ + if (response->header.iu_type != PQI_RESPONSE_IU_GENERAL_ADMIN) + return -EINVAL; + + if (get_unaligned_le16(&response->header.iu_length) != + PQI_GENERAL_ADMIN_IU_LENGTH) + return -EINVAL; + + if (response->function_code != expected_function_code) + return -EINVAL; + + if (response->status != PQI_GENERAL_ADMIN_STATUS_SUCCESS) + return -EINVAL; + + return 0; +} + +static int pqi_submit_admin_request_synchronous( + struct pqi_ctrl_info *ctrl_info, + struct pqi_general_admin_request *request, + struct pqi_general_admin_response *response) +{ + int rc; + + pqi_submit_admin_request(ctrl_info, request); + + rc = pqi_poll_for_admin_response(ctrl_info, response); + + if (rc == 0) + rc = pqi_validate_admin_response(response, + request->function_code); + + return rc; +} + +static int pqi_report_device_capability(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct pqi_general_admin_request request; + struct pqi_general_admin_response response; + struct pqi_device_capability *capability; + struct pqi_iu_layer_descriptor *sop_iu_layer_descriptor; + + capability = kmalloc(sizeof(*capability), GFP_KERNEL); + if (!capability) + return -ENOMEM; + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = + PQI_GENERAL_ADMIN_FUNCTION_REPORT_DEVICE_CAPABILITY; + put_unaligned_le32(sizeof(*capability), + &request.data.report_device_capability.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + &request.data.report_device_capability.sg_descriptor, + capability, sizeof(*capability), + PCI_DMA_FROMDEVICE); + if (rc) + goto out; + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + + pqi_pci_unmap(ctrl_info->pci_dev, + &request.data.report_device_capability.sg_descriptor, 1, + PCI_DMA_FROMDEVICE); + + if (rc) + goto out; + + if (response.status != PQI_GENERAL_ADMIN_STATUS_SUCCESS) { + rc = -EIO; + goto out; + } + + ctrl_info->max_inbound_queues = + get_unaligned_le16(&capability->max_inbound_queues); + ctrl_info->max_elements_per_iq = + get_unaligned_le16(&capability->max_elements_per_iq); + ctrl_info->max_iq_element_length = + get_unaligned_le16(&capability->max_iq_element_length) + * 16; + ctrl_info->max_outbound_queues = + get_unaligned_le16(&capability->max_outbound_queues); + ctrl_info->max_elements_per_oq = + get_unaligned_le16(&capability->max_elements_per_oq); + ctrl_info->max_oq_element_length = + get_unaligned_le16(&capability->max_oq_element_length) + * 16; + + sop_iu_layer_descriptor = + &capability->iu_layer_descriptors[PQI_PROTOCOL_SOP]; + + ctrl_info->max_inbound_iu_length_per_firmware = + get_unaligned_le16( + &sop_iu_layer_descriptor->max_inbound_iu_length); + ctrl_info->inbound_spanning_supported = + sop_iu_layer_descriptor->inbound_spanning_supported; + ctrl_info->outbound_spanning_supported = + sop_iu_layer_descriptor->outbound_spanning_supported; + +out: + kfree(capability); + + return rc; +} + +static int pqi_validate_device_capability(struct pqi_ctrl_info *ctrl_info) +{ + if (ctrl_info->max_iq_element_length < + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) { + dev_err(&ctrl_info->pci_dev->dev, + "max. inbound queue element length of %d is less than the required length of %d\n", + ctrl_info->max_iq_element_length, + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + return -EINVAL; + } + + if (ctrl_info->max_oq_element_length < + PQI_OPERATIONAL_OQ_ELEMENT_LENGTH) { + dev_err(&ctrl_info->pci_dev->dev, + "max. outbound queue element length of %d is less than the required length of %d\n", + ctrl_info->max_oq_element_length, + PQI_OPERATIONAL_OQ_ELEMENT_LENGTH); + return -EINVAL; + } + + if (ctrl_info->max_inbound_iu_length_per_firmware < + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) { + dev_err(&ctrl_info->pci_dev->dev, + "max. inbound IU length of %u is less than the min. required length of %d\n", + ctrl_info->max_inbound_iu_length_per_firmware, + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + return -EINVAL; + } + + return 0; +} + +static int pqi_delete_operational_queue(struct pqi_ctrl_info *ctrl_info, + bool inbound_queue, u16 queue_id) +{ + struct pqi_general_admin_request request; + struct pqi_general_admin_response response; + + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + if (inbound_queue) + request.function_code = + PQI_GENERAL_ADMIN_FUNCTION_DELETE_IQ; + else + request.function_code = + PQI_GENERAL_ADMIN_FUNCTION_DELETE_OQ; + put_unaligned_le16(queue_id, + &request.data.delete_operational_queue.queue_id); + + return pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); +} + +static int pqi_create_event_queue(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct pqi_event_queue *event_queue; + struct pqi_general_admin_request request; + struct pqi_general_admin_response response; + + event_queue = &ctrl_info->event_queue; + + /* + * Create OQ (Outbound Queue - device to host queue) to dedicate + * to events. + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ; + put_unaligned_le16(event_queue->oq_id, + &request.data.create_operational_oq.queue_id); + put_unaligned_le64((u64)event_queue->oq_element_array_bus_addr, + &request.data.create_operational_oq.element_array_addr); + put_unaligned_le64((u64)event_queue->oq_pi_bus_addr, + &request.data.create_operational_oq.pi_addr); + put_unaligned_le16(PQI_NUM_EVENT_QUEUE_ELEMENTS, + &request.data.create_operational_oq.num_elements); + put_unaligned_le16(PQI_EVENT_OQ_ELEMENT_LENGTH / 16, + &request.data.create_operational_oq.element_length); + request.data.create_operational_oq.queue_protocol = PQI_PROTOCOL_SOP; + put_unaligned_le16(event_queue->int_msg_num, + &request.data.create_operational_oq.int_msg_num); + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) + return rc; + + event_queue->oq_ci = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + get_unaligned_le64( + &response.data.create_operational_oq.oq_ci_offset); + + return 0; +} + +static int pqi_create_queue_group(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + int rc; + struct pqi_queue_group *queue_group; + struct pqi_general_admin_request request; + struct pqi_general_admin_response response; + + i = ctrl_info->num_active_queue_groups; + queue_group = &ctrl_info->queue_groups[i]; + + /* + * Create IQ (Inbound Queue - host to device queue) for + * RAID path. + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ; + put_unaligned_le16(queue_group->iq_id[RAID_PATH], + &request.data.create_operational_iq.queue_id); + put_unaligned_le64( + (u64)queue_group->iq_element_array_bus_addr[RAID_PATH], + &request.data.create_operational_iq.element_array_addr); + put_unaligned_le64((u64)queue_group->iq_ci_bus_addr[RAID_PATH], + &request.data.create_operational_iq.ci_addr); + put_unaligned_le16(ctrl_info->num_elements_per_iq, + &request.data.create_operational_iq.num_elements); + put_unaligned_le16(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH / 16, + &request.data.create_operational_iq.element_length); + request.data.create_operational_iq.queue_protocol = PQI_PROTOCOL_SOP; + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating inbound RAID queue\n"); + return rc; + } + + queue_group->iq_pi[RAID_PATH] = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + get_unaligned_le64( + &response.data.create_operational_iq.iq_pi_offset); + + /* + * Create IQ (Inbound Queue - host to device queue) for + * Advanced I/O (AIO) path. + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ; + put_unaligned_le16(queue_group->iq_id[AIO_PATH], + &request.data.create_operational_iq.queue_id); + put_unaligned_le64((u64)queue_group-> + iq_element_array_bus_addr[AIO_PATH], + &request.data.create_operational_iq.element_array_addr); + put_unaligned_le64((u64)queue_group->iq_ci_bus_addr[AIO_PATH], + &request.data.create_operational_iq.ci_addr); + put_unaligned_le16(ctrl_info->num_elements_per_iq, + &request.data.create_operational_iq.num_elements); + put_unaligned_le16(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH / 16, + &request.data.create_operational_iq.element_length); + request.data.create_operational_iq.queue_protocol = PQI_PROTOCOL_SOP; + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating inbound AIO queue\n"); + goto delete_inbound_queue_raid; + } + + queue_group->iq_pi[AIO_PATH] = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + get_unaligned_le64( + &response.data.create_operational_iq.iq_pi_offset); + + /* + * Designate the 2nd IQ as the AIO path. By default, all IQs are + * assumed to be for RAID path I/O unless we change the queue's + * property. + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CHANGE_IQ_PROPERTY; + put_unaligned_le16(queue_group->iq_id[AIO_PATH], + &request.data.change_operational_iq_properties.queue_id); + put_unaligned_le32(PQI_IQ_PROPERTY_IS_AIO_QUEUE, + &request.data.change_operational_iq_properties.vendor_specific); + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error changing queue property\n"); + goto delete_inbound_queue_aio; + } + + /* + * Create OQ (Outbound Queue - device to host queue). + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ; + put_unaligned_le16(queue_group->oq_id, + &request.data.create_operational_oq.queue_id); + put_unaligned_le64((u64)queue_group->oq_element_array_bus_addr, + &request.data.create_operational_oq.element_array_addr); + put_unaligned_le64((u64)queue_group->oq_pi_bus_addr, + &request.data.create_operational_oq.pi_addr); + put_unaligned_le16(ctrl_info->num_elements_per_oq, + &request.data.create_operational_oq.num_elements); + put_unaligned_le16(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH / 16, + &request.data.create_operational_oq.element_length); + request.data.create_operational_oq.queue_protocol = PQI_PROTOCOL_SOP; + put_unaligned_le16(queue_group->int_msg_num, + &request.data.create_operational_oq.int_msg_num); + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating outbound queue\n"); + goto delete_inbound_queue_aio; + } + + queue_group->oq_ci = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + get_unaligned_le64( + &response.data.create_operational_oq.oq_ci_offset); + + ctrl_info->num_active_queue_groups++; + + return 0; + +delete_inbound_queue_aio: + pqi_delete_operational_queue(ctrl_info, true, + queue_group->iq_id[AIO_PATH]); + +delete_inbound_queue_raid: + pqi_delete_operational_queue(ctrl_info, true, + queue_group->iq_id[RAID_PATH]); + + return rc; +} + +static int pqi_create_queues(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + unsigned int i; + + rc = pqi_create_event_queue(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating event queue\n"); + return rc; + } + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + rc = pqi_create_queue_group(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating queue group number %u/%u\n", + i, ctrl_info->num_queue_groups); + return rc; + } + } + + return 0; +} + +#define PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH \ + (offsetof(struct pqi_event_config, descriptors) + \ + (PQI_MAX_EVENT_DESCRIPTORS * sizeof(struct pqi_event_descriptor))) + +static int pqi_configure_events(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + unsigned int i; + struct pqi_event_config *event_config; + struct pqi_general_management_request request; + + event_config = kmalloc(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + GFP_KERNEL); + if (!event_config) + return -ENOMEM; + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_REPORT_VENDOR_EVENT_CONFIG; + put_unaligned_le16(offsetof(struct pqi_general_management_request, + data.report_event_configuration.sg_descriptors[1]) - + PQI_REQUEST_HEADER_LENGTH, &request.header.iu_length); + put_unaligned_le32(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + &request.data.report_event_configuration.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + request.data.report_event_configuration.sg_descriptors, + event_config, PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + PCI_DMA_FROMDEVICE); + if (rc) + goto out; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + 0, NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, + request.data.report_event_configuration.sg_descriptors, 1, + PCI_DMA_FROMDEVICE); + + if (rc) + goto out; + + for (i = 0; i < event_config->num_event_descriptors; i++) + put_unaligned_le16(ctrl_info->event_queue.oq_id, + &event_config->descriptors[i].oq_id); + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_SET_VENDOR_EVENT_CONFIG; + put_unaligned_le16(offsetof(struct pqi_general_management_request, + data.report_event_configuration.sg_descriptors[1]) - + PQI_REQUEST_HEADER_LENGTH, &request.header.iu_length); + put_unaligned_le32(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + &request.data.report_event_configuration.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + request.data.report_event_configuration.sg_descriptors, + event_config, PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + PCI_DMA_TODEVICE); + if (rc) + goto out; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, + request.data.report_event_configuration.sg_descriptors, 1, + PCI_DMA_TODEVICE); + +out: + kfree(event_config); + + return rc; +} + +static void pqi_free_all_io_requests(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + struct device *dev; + size_t sg_chain_buffer_length; + struct pqi_io_request *io_request; + + if (!ctrl_info->io_request_pool) + return; + + dev = &ctrl_info->pci_dev->dev; + sg_chain_buffer_length = ctrl_info->sg_chain_buffer_length; + io_request = ctrl_info->io_request_pool; + + for (i = 0; i < ctrl_info->max_io_slots; i++) { + kfree(io_request->iu); + if (!io_request->sg_chain_buffer) + break; + dma_free_coherent(dev, sg_chain_buffer_length, + io_request->sg_chain_buffer, + io_request->sg_chain_buffer_dma_handle); + io_request++; + } + + kfree(ctrl_info->io_request_pool); + ctrl_info->io_request_pool = NULL; +} + +static inline int pqi_alloc_error_buffer(struct pqi_ctrl_info *ctrl_info) +{ + ctrl_info->error_buffer = dma_zalloc_coherent(&ctrl_info->pci_dev->dev, + ctrl_info->error_buffer_length, + &ctrl_info->error_buffer_dma_handle, GFP_KERNEL); + + if (!ctrl_info->error_buffer) + return -ENOMEM; + + return 0; +} + +static int pqi_alloc_io_resources(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + void *sg_chain_buffer; + size_t sg_chain_buffer_length; + dma_addr_t sg_chain_buffer_dma_handle; + struct device *dev; + struct pqi_io_request *io_request; + + ctrl_info->io_request_pool = kzalloc(ctrl_info->max_io_slots * + sizeof(ctrl_info->io_request_pool[0]), GFP_KERNEL); + + if (!ctrl_info->io_request_pool) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate I/O request pool\n"); + goto error; + } + + dev = &ctrl_info->pci_dev->dev; + sg_chain_buffer_length = ctrl_info->sg_chain_buffer_length; + io_request = ctrl_info->io_request_pool; + + for (i = 0; i < ctrl_info->max_io_slots; i++) { + io_request->iu = + kmalloc(ctrl_info->max_inbound_iu_length, GFP_KERNEL); + + if (!io_request->iu) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate IU buffers\n"); + goto error; + } + + sg_chain_buffer = dma_alloc_coherent(dev, + sg_chain_buffer_length, &sg_chain_buffer_dma_handle, + GFP_KERNEL); + + if (!sg_chain_buffer) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate PQI scatter-gather chain buffers\n"); + goto error; + } + + io_request->index = i; + io_request->sg_chain_buffer = sg_chain_buffer; + io_request->sg_chain_buffer_dma_handle = + sg_chain_buffer_dma_handle; + io_request++; + } + + return 0; + +error: + pqi_free_all_io_requests(ctrl_info); + + return -ENOMEM; +} + +/* + * Calculate required resources that are sized based on max. outstanding + * requests and max. transfer size. + */ + +static void pqi_calculate_io_resources(struct pqi_ctrl_info *ctrl_info) +{ + u32 max_transfer_size; + u32 max_sg_entries; + + ctrl_info->scsi_ml_can_queue = + ctrl_info->max_outstanding_requests - PQI_RESERVED_IO_SLOTS; + ctrl_info->max_io_slots = ctrl_info->max_outstanding_requests; + + ctrl_info->error_buffer_length = + ctrl_info->max_io_slots * PQI_ERROR_BUFFER_ELEMENT_LENGTH; + + max_transfer_size = + min(ctrl_info->max_transfer_size, PQI_MAX_TRANSFER_SIZE); + + max_sg_entries = max_transfer_size / PAGE_SIZE; + + /* +1 to cover when the buffer is not page-aligned. */ + max_sg_entries++; + + max_sg_entries = min(ctrl_info->max_sg_entries, max_sg_entries); + + max_transfer_size = (max_sg_entries - 1) * PAGE_SIZE; + + ctrl_info->sg_chain_buffer_length = + max_sg_entries * sizeof(struct pqi_sg_descriptor); + ctrl_info->sg_tablesize = max_sg_entries; + ctrl_info->max_sectors = max_transfer_size / 512; +} + +static void pqi_calculate_queue_resources(struct pqi_ctrl_info *ctrl_info) +{ + int num_cpus; + int max_queue_groups; + int num_queue_groups; + u16 num_elements_per_iq; + u16 num_elements_per_oq; + + max_queue_groups = min(ctrl_info->max_inbound_queues / 2, + ctrl_info->max_outbound_queues - 1); + max_queue_groups = min(max_queue_groups, PQI_MAX_QUEUE_GROUPS); + + num_cpus = num_online_cpus(); + num_queue_groups = min(num_cpus, ctrl_info->max_msix_vectors); + num_queue_groups = min(num_queue_groups, max_queue_groups); + + ctrl_info->num_queue_groups = num_queue_groups; + + if (ctrl_info->max_inbound_iu_length_per_firmware == 256 && + ctrl_info->outbound_spanning_supported) { + /* + * TEMPHACK + * This is older f/w that doesn't actually support spanning. + */ + ctrl_info->max_inbound_iu_length = + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH; + } else { + /* + * Make sure that the max. inbound IU length is an even multiple + * of our inbound element length. + */ + ctrl_info->max_inbound_iu_length = + (ctrl_info->max_inbound_iu_length_per_firmware / + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) * + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH; + } + + num_elements_per_iq = + (ctrl_info->max_inbound_iu_length / + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + /* Add one because one element in each queue is unusable. */ + num_elements_per_iq++; + + num_elements_per_iq = min(num_elements_per_iq, + ctrl_info->max_elements_per_iq); + + num_elements_per_oq = ((num_elements_per_iq - 1) * 2) + 1; + num_elements_per_oq = min(num_elements_per_oq, + ctrl_info->max_elements_per_oq); + + ctrl_info->num_elements_per_iq = num_elements_per_iq; + ctrl_info->num_elements_per_oq = num_elements_per_oq; + + ctrl_info->max_sg_per_iu = + ((ctrl_info->max_inbound_iu_length - + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) / + sizeof(struct pqi_sg_descriptor)) + + PQI_MAX_EMBEDDED_SG_DESCRIPTORS; +} + +static inline void pqi_set_sg_descriptor( + struct pqi_sg_descriptor *sg_descriptor, struct scatterlist *sg) +{ + u64 address = (u64)sg_dma_address(sg); + unsigned int length = sg_dma_len(sg); + + put_unaligned_le64(address, &sg_descriptor->address); + put_unaligned_le32(length, &sg_descriptor->length); + put_unaligned_le32(0, &sg_descriptor->flags); +} + +static int pqi_build_raid_sg_list(struct pqi_ctrl_info *ctrl_info, + struct pqi_raid_path_request *request, struct scsi_cmnd *scmd, + struct pqi_io_request *io_request) +{ + int i; + u16 iu_length; + int sg_count; + bool chained; + unsigned int num_sg_in_iu; + unsigned int max_sg_per_iu; + struct scatterlist *sg; + struct pqi_sg_descriptor *sg_descriptor; + + sg_count = scsi_dma_map(scmd); + if (sg_count < 0) + return sg_count; + + iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) - + PQI_REQUEST_HEADER_LENGTH; + + if (sg_count == 0) + goto out; + + sg = scsi_sglist(scmd); + sg_descriptor = request->sg_descriptors; + max_sg_per_iu = ctrl_info->max_sg_per_iu - 1; + chained = false; + num_sg_in_iu = 0; + i = 0; + + while (1) { + pqi_set_sg_descriptor(sg_descriptor, sg); + if (!chained) + num_sg_in_iu++; + i++; + if (i == sg_count) + break; + sg_descriptor++; + if (i == max_sg_per_iu) { + put_unaligned_le64( + (u64)io_request->sg_chain_buffer_dma_handle, + &sg_descriptor->address); + put_unaligned_le32((sg_count - num_sg_in_iu) + * sizeof(*sg_descriptor), + &sg_descriptor->length); + put_unaligned_le32(CISS_SG_CHAIN, + &sg_descriptor->flags); + chained = true; + num_sg_in_iu++; + sg_descriptor = io_request->sg_chain_buffer; + } + sg = sg_next(sg); + } + + put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags); + request->partial = chained; + iu_length += num_sg_in_iu * sizeof(*sg_descriptor); + +out: + put_unaligned_le16(iu_length, &request->header.iu_length); + + return 0; +} + +static int pqi_build_aio_sg_list(struct pqi_ctrl_info *ctrl_info, + struct pqi_aio_path_request *request, struct scsi_cmnd *scmd, + struct pqi_io_request *io_request) +{ + int i; + u16 iu_length; + int sg_count; + unsigned int num_sg_in_iu = 0; + struct scatterlist *sg; + struct pqi_sg_descriptor *sg_descriptor; + + sg_count = scsi_dma_map(scmd); + if (sg_count < 0) + return sg_count; + if (sg_count == 0) + goto out; + + if (sg_count <= ctrl_info->max_sg_per_iu) { + sg_descriptor = &request->sg_descriptors[0]; + scsi_for_each_sg(scmd, sg, sg_count, i) { + pqi_set_sg_descriptor(sg_descriptor, sg); + sg_descriptor++; + } + put_unaligned_le32(CISS_SG_LAST, + &request->sg_descriptors[sg_count - 1].flags); + num_sg_in_iu = sg_count; + } else { + sg_descriptor = &request->sg_descriptors[0]; + put_unaligned_le64((u64)io_request->sg_chain_buffer_dma_handle, + &sg_descriptor->address); + put_unaligned_le32(sg_count * sizeof(*sg_descriptor), + &sg_descriptor->length); + put_unaligned_le32(CISS_SG_CHAIN, &sg_descriptor->flags); + + sg_descriptor = io_request->sg_chain_buffer; + scsi_for_each_sg(scmd, sg, sg_count, i) { + pqi_set_sg_descriptor(sg_descriptor, sg); + sg_descriptor++; + } + put_unaligned_le32(CISS_SG_LAST, + &io_request->sg_chain_buffer[sg_count - 1].flags); + num_sg_in_iu = 1; + request->partial = 1; + } + +out: + iu_length = offsetof(struct pqi_aio_path_request, sg_descriptors) - + PQI_REQUEST_HEADER_LENGTH; + iu_length += num_sg_in_iu * sizeof(*sg_descriptor); + put_unaligned_le16(iu_length, &request->header.iu_length); + request->num_sg_descriptors = num_sg_in_iu; + + return 0; +} + +static void pqi_raid_io_complete(struct pqi_io_request *io_request, + void *context) +{ + struct scsi_cmnd *scmd; + + scmd = io_request->scmd; + pqi_free_io_request(io_request); + scsi_dma_unmap(scmd); + pqi_scsi_done(scmd); +} + +static int pqi_raid_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, + struct pqi_queue_group *queue_group) +{ + int rc; + size_t cdb_length; + struct pqi_io_request *io_request; + struct pqi_raid_path_request *request; + + io_request = pqi_alloc_io_request(ctrl_info); + io_request->io_complete_callback = pqi_raid_io_complete; + io_request->scmd = scmd; + + scmd->host_scribble = (unsigned char *)io_request; + + request = io_request->iu; + memset(request, 0, + offsetof(struct pqi_raid_path_request, sg_descriptors)); + + request->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO; + put_unaligned_le32(scsi_bufflen(scmd), &request->buffer_length); + request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + put_unaligned_le16(io_request->index, &request->request_id); + request->error_index = request->request_id; + memcpy(request->lun_number, device->scsi3addr, + sizeof(request->lun_number)); + + cdb_length = min_t(size_t, scmd->cmd_len, sizeof(request->cdb)); + memcpy(request->cdb, scmd->cmnd, cdb_length); + + switch (cdb_length) { + case 6: + case 10: + case 12: + case 16: + /* No bytes in the Additional CDB bytes field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_0; + break; + case 20: + /* 4 bytes in the Additional cdb field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_4; + break; + case 24: + /* 8 bytes in the Additional cdb field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_8; + break; + case 28: + /* 12 bytes in the Additional cdb field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_12; + break; + case 32: + default: + /* 16 bytes in the Additional cdb field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_16; + break; + } + + switch (scmd->sc_data_direction) { + case DMA_TO_DEVICE: + request->data_direction = SOP_READ_FLAG; + break; + case DMA_FROM_DEVICE: + request->data_direction = SOP_WRITE_FLAG; + break; + case DMA_NONE: + request->data_direction = SOP_NO_DIRECTION_FLAG; + break; + case DMA_BIDIRECTIONAL: + request->data_direction = SOP_BIDIRECTIONAL; + break; + default: + dev_err(&ctrl_info->pci_dev->dev, + "unknown data direction: %d\n", + scmd->sc_data_direction); + WARN_ON(scmd->sc_data_direction); + break; + } + + rc = pqi_build_raid_sg_list(ctrl_info, request, scmd, io_request); + if (rc) { + pqi_free_io_request(io_request); + return SCSI_MLQUEUE_HOST_BUSY; + } + + pqi_start_io(ctrl_info, queue_group, RAID_PATH, io_request); + + return 0; +} + +static void pqi_aio_io_complete(struct pqi_io_request *io_request, + void *context) +{ + struct scsi_cmnd *scmd; + + scmd = io_request->scmd; + scsi_dma_unmap(scmd); + if (io_request->status == -EAGAIN) + set_host_byte(scmd, DID_IMM_RETRY); + pqi_free_io_request(io_request); + pqi_scsi_done(scmd); +} + +static inline int pqi_aio_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, + struct pqi_queue_group *queue_group) +{ + return pqi_aio_submit_io(ctrl_info, scmd, device->aio_handle, + scmd->cmnd, scmd->cmd_len, queue_group, NULL); +} + +static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, + struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb, + unsigned int cdb_length, struct pqi_queue_group *queue_group, + struct pqi_encryption_info *encryption_info) +{ + int rc; + struct pqi_io_request *io_request; + struct pqi_aio_path_request *request; + + io_request = pqi_alloc_io_request(ctrl_info); + io_request->io_complete_callback = pqi_aio_io_complete; + io_request->scmd = scmd; + + scmd->host_scribble = (unsigned char *)io_request; + + request = io_request->iu; + memset(request, 0, + offsetof(struct pqi_raid_path_request, sg_descriptors)); + + request->header.iu_type = PQI_REQUEST_IU_AIO_PATH_IO; + put_unaligned_le32(aio_handle, &request->nexus_id); + put_unaligned_le32(scsi_bufflen(scmd), &request->buffer_length); + request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + put_unaligned_le16(io_request->index, &request->request_id); + request->error_index = request->request_id; + if (cdb_length > sizeof(request->cdb)) + cdb_length = sizeof(request->cdb); + request->cdb_length = cdb_length; + memcpy(request->cdb, cdb, cdb_length); + + switch (scmd->sc_data_direction) { + case DMA_TO_DEVICE: + request->data_direction = SOP_READ_FLAG; + break; + case DMA_FROM_DEVICE: + request->data_direction = SOP_WRITE_FLAG; + break; + case DMA_NONE: + request->data_direction = SOP_NO_DIRECTION_FLAG; + break; + case DMA_BIDIRECTIONAL: + request->data_direction = SOP_BIDIRECTIONAL; + break; + default: + dev_err(&ctrl_info->pci_dev->dev, + "unknown data direction: %d\n", + scmd->sc_data_direction); + WARN_ON(scmd->sc_data_direction); + break; + } + + if (encryption_info) { + request->encryption_enable = true; + put_unaligned_le16(encryption_info->data_encryption_key_index, + &request->data_encryption_key_index); + put_unaligned_le32(encryption_info->encrypt_tweak_lower, + &request->encrypt_tweak_lower); + put_unaligned_le32(encryption_info->encrypt_tweak_upper, + &request->encrypt_tweak_upper); + } + + rc = pqi_build_aio_sg_list(ctrl_info, request, scmd, io_request); + if (rc) { + pqi_free_io_request(io_request); + return SCSI_MLQUEUE_HOST_BUSY; + } + + pqi_start_io(ctrl_info, queue_group, AIO_PATH, io_request); + + return 0; +} + +static int pqi_scsi_queue_command(struct Scsi_Host *shost, + struct scsi_cmnd *scmd) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + u16 hwq; + struct pqi_queue_group *queue_group; + bool raid_bypassed; + + device = scmd->device->hostdata; + + if (device->reset_in_progress) { + set_host_byte(scmd, DID_RESET); + pqi_scsi_done(scmd); + return 0; + } + + ctrl_info = shost_to_hba(shost); + + if (pqi_ctrl_offline(ctrl_info)) { + set_host_byte(scmd, DID_NO_CONNECT); + pqi_scsi_done(scmd); + return 0; + } + + hwq = blk_mq_unique_tag_to_hwq(blk_mq_unique_tag(scmd->request)); + if (hwq >= ctrl_info->num_queue_groups) + hwq = 0; + + queue_group = &ctrl_info->queue_groups[hwq]; + + if (pqi_is_logical_device(device)) { + raid_bypassed = false; + if (device->offload_enabled && + scmd->request->cmd_type == REQ_TYPE_FS) { + rc = pqi_raid_bypass_submit_scsi_cmd(ctrl_info, device, + scmd, queue_group); + if (rc == 0 || + rc == SCSI_MLQUEUE_HOST_BUSY || + rc == SAM_STAT_CHECK_CONDITION || + rc == SAM_STAT_RESERVATION_CONFLICT) + raid_bypassed = true; + } + if (!raid_bypassed) + rc = pqi_raid_submit_scsi_cmd(ctrl_info, device, scmd, + queue_group); + } else { + if (device->aio_enabled) + rc = pqi_aio_submit_scsi_cmd(ctrl_info, device, scmd, + queue_group); + else + rc = pqi_raid_submit_scsi_cmd(ctrl_info, device, scmd, + queue_group); + } + + return rc; +} + +static inline void pqi_complete_queued_requests_queue_group( + struct pqi_queue_group *queue_group, + struct pqi_scsi_dev *device_in_reset) +{ + unsigned int path; + unsigned long flags; + struct pqi_io_request *io_request; + struct pqi_io_request *next; + struct scsi_cmnd *scmd; + struct pqi_scsi_dev *device; + + for (path = 0; path < 2; path++) { + spin_lock_irqsave(&queue_group->submit_lock[path], flags); + + list_for_each_entry_safe(io_request, next, + &queue_group->request_list[path], + request_list_entry) { + scmd = io_request->scmd; + if (!scmd) + continue; + device = scmd->device->hostdata; + if (device == device_in_reset) { + set_host_byte(scmd, DID_RESET); + pqi_scsi_done(scmd); + list_del(&io_request-> + request_list_entry); + } + } + + spin_unlock_irqrestore(&queue_group->submit_lock[path], flags); + } +} + +static void pqi_complete_queued_requests(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device_in_reset) +{ + unsigned int i; + struct pqi_queue_group *queue_group; + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + pqi_complete_queued_requests_queue_group(queue_group, + device_in_reset); + } +} + +static void pqi_reset_lun_complete(struct pqi_io_request *io_request, + void *context) +{ + struct completion *waiting = context; + + complete(waiting); +} + +static int pqi_reset_lun(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + struct pqi_io_request *io_request; + DECLARE_COMPLETION_ONSTACK(wait); + struct pqi_task_management_request *request; + + down(&ctrl_info->lun_reset_sem); + + io_request = pqi_alloc_io_request(ctrl_info); + io_request->io_complete_callback = pqi_reset_lun_complete; + io_request->context = &wait; + + request = io_request->iu; + memset(request, 0, sizeof(*request)); + + request->header.iu_type = PQI_REQUEST_IU_TASK_MANAGEMENT; + put_unaligned_le16(sizeof(*request) - PQI_REQUEST_HEADER_LENGTH, + &request->header.iu_length); + put_unaligned_le16(io_request->index, &request->request_id); + memcpy(request->lun_number, device->scsi3addr, + sizeof(request->lun_number)); + request->task_management_function = SOP_TASK_MANAGEMENT_LUN_RESET; + + pqi_start_io(ctrl_info, + &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH, + io_request); + + if (!wait_for_completion_io_timeout(&wait, + msecs_to_jiffies(PQI_ABORT_TIMEOUT_MSECS))) { + rc = -ETIMEDOUT; + } else { + rc = io_request->status; + } + + pqi_free_io_request(io_request); + up(&ctrl_info->lun_reset_sem); + + return rc; +} + +/* Performs a reset at the LUN level. */ + +static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + + pqi_check_ctrl_health(ctrl_info); + if (pqi_ctrl_offline(ctrl_info)) + return FAILED; + + device->reset_in_progress = true; + pqi_complete_queued_requests(ctrl_info, device); + rc = pqi_reset_lun(ctrl_info, device); + device->reset_in_progress = false; + + if (rc) + return FAILED; + + return SUCCESS; +} + +static int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + + ctrl_info = shost_to_hba(scmd->device->host); + + device = scmd->device->hostdata; + + dev_err(&ctrl_info->pci_dev->dev, + "resetting scsi %d:%d:%d:%d\n", + ctrl_info->scsi_host->host_no, + device->bus, device->target, device->lun); + + rc = pqi_device_reset(ctrl_info, device); + + dev_err(&ctrl_info->pci_dev->dev, + "reset of scsi %d:%d:%d:%d: %s\n", + ctrl_info->scsi_host->host_no, + device->bus, device->target, device->lun, + rc == SUCCESS ? "SUCCESS" : "FAILED"); + + return rc; +} + +static int pqi_slave_alloc(struct scsi_device *sdev) +{ + struct pqi_scsi_dev *device; + unsigned long flags; + struct pqi_ctrl_info *ctrl_info; + struct scsi_target *starget; + struct sas_rphy *rphy; + + ctrl_info = shost_to_hba(sdev->host); + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + if (sdev_channel(sdev) == PQI_PHYSICAL_DEVICE_BUS) { + starget = scsi_target(sdev); + rphy = target_to_rphy(starget); + device = pqi_find_device_by_sas_rphy(ctrl_info, rphy); + if (device) { + device->target = sdev_id(sdev); + device->lun = sdev->lun; + device->target_lun_valid = true; + } + } else { + device = pqi_find_scsi_dev(ctrl_info, sdev_channel(sdev), + sdev_id(sdev), sdev->lun); + } + + if (device && device->expose_device) { + sdev->hostdata = device; + device->sdev = sdev; + if (device->queue_depth) { + device->advertised_queue_depth = device->queue_depth; + scsi_change_queue_depth(sdev, + device->advertised_queue_depth); + } + } + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + return 0; +} + +static int pqi_slave_configure(struct scsi_device *sdev) +{ + struct pqi_scsi_dev *device; + + device = sdev->hostdata; + if (!device->expose_device) + sdev->no_uld_attach = true; + + return 0; +} + +static int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info, + void __user *arg) +{ + struct pci_dev *pci_dev; + u32 subsystem_vendor; + u32 subsystem_device; + cciss_pci_info_struct pciinfo; + + if (!arg) + return -EINVAL; + + pci_dev = ctrl_info->pci_dev; + + pciinfo.domain = pci_domain_nr(pci_dev->bus); + pciinfo.bus = pci_dev->bus->number; + pciinfo.dev_fn = pci_dev->devfn; + subsystem_vendor = pci_dev->subsystem_vendor; + subsystem_device = pci_dev->subsystem_device; + pciinfo.board_id = ((subsystem_device << 16) & 0xffff0000) | + subsystem_vendor; + + if (copy_to_user(arg, &pciinfo, sizeof(pciinfo))) + return -EFAULT; + + return 0; +} + +static int pqi_getdrivver_ioctl(void __user *arg) +{ + u32 version; + + if (!arg) + return -EINVAL; + + version = (DRIVER_MAJOR << 28) | (DRIVER_MINOR << 24) | + (DRIVER_RELEASE << 16) | DRIVER_REVISION; + + if (copy_to_user(arg, &version, sizeof(version))) + return -EFAULT; + + return 0; +} + +struct ciss_error_info { + u8 scsi_status; + int command_status; + size_t sense_data_length; +}; + +static void pqi_error_info_to_ciss(struct pqi_raid_error_info *pqi_error_info, + struct ciss_error_info *ciss_error_info) +{ + int ciss_cmd_status; + size_t sense_data_length; + + switch (pqi_error_info->data_out_result) { + case PQI_DATA_IN_OUT_GOOD: + ciss_cmd_status = CISS_CMD_STATUS_SUCCESS; + break; + case PQI_DATA_IN_OUT_UNDERFLOW: + ciss_cmd_status = CISS_CMD_STATUS_DATA_UNDERRUN; + break; + case PQI_DATA_IN_OUT_BUFFER_OVERFLOW: + ciss_cmd_status = CISS_CMD_STATUS_DATA_OVERRUN; + break; + case PQI_DATA_IN_OUT_PROTOCOL_ERROR: + case PQI_DATA_IN_OUT_BUFFER_ERROR: + case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_DESCRIPTOR_AREA: + case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_BRIDGE: + case PQI_DATA_IN_OUT_ERROR: + ciss_cmd_status = CISS_CMD_STATUS_PROTOCOL_ERROR; + break; + case PQI_DATA_IN_OUT_HARDWARE_ERROR: + case PQI_DATA_IN_OUT_PCIE_FABRIC_ERROR: + case PQI_DATA_IN_OUT_PCIE_COMPLETION_TIMEOUT: + case PQI_DATA_IN_OUT_PCIE_COMPLETER_ABORT_RECEIVED: + case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST_RECEIVED: + case PQI_DATA_IN_OUT_PCIE_ECRC_CHECK_FAILED: + case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST: + case PQI_DATA_IN_OUT_PCIE_ACS_VIOLATION: + case PQI_DATA_IN_OUT_PCIE_TLP_PREFIX_BLOCKED: + case PQI_DATA_IN_OUT_PCIE_POISONED_MEMORY_READ: + ciss_cmd_status = CISS_CMD_STATUS_HARDWARE_ERROR; + break; + case PQI_DATA_IN_OUT_UNSOLICITED_ABORT: + ciss_cmd_status = CISS_CMD_STATUS_UNSOLICITED_ABORT; + break; + case PQI_DATA_IN_OUT_ABORTED: + ciss_cmd_status = CISS_CMD_STATUS_ABORTED; + break; + case PQI_DATA_IN_OUT_TIMEOUT: + ciss_cmd_status = CISS_CMD_STATUS_TIMEOUT; + break; + default: + ciss_cmd_status = CISS_CMD_STATUS_TARGET_STATUS; + break; + } + + sense_data_length = + get_unaligned_le16(&pqi_error_info->sense_data_length); + if (sense_data_length == 0) + sense_data_length = + get_unaligned_le16(&pqi_error_info->response_data_length); + if (sense_data_length) + if (sense_data_length > sizeof(pqi_error_info->data)) + sense_data_length = sizeof(pqi_error_info->data); + + ciss_error_info->scsi_status = pqi_error_info->status; + ciss_error_info->command_status = ciss_cmd_status; + ciss_error_info->sense_data_length = sense_data_length; +} + +static int pqi_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg) +{ + int rc; + char *kernel_buffer = NULL; + u16 iu_length; + size_t sense_data_length; + IOCTL_Command_struct iocommand; + struct pqi_raid_path_request request; + struct pqi_raid_error_info pqi_error_info; + struct ciss_error_info ciss_error_info; + + if (pqi_ctrl_offline(ctrl_info)) + return -ENXIO; + if (!arg) + return -EINVAL; + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + if (copy_from_user(&iocommand, arg, sizeof(iocommand))) + return -EFAULT; + if (iocommand.buf_size < 1 && + iocommand.Request.Type.Direction != XFER_NONE) + return -EINVAL; + if (iocommand.Request.CDBLen > sizeof(request.cdb)) + return -EINVAL; + if (iocommand.Request.Type.Type != TYPE_CMD) + return -EINVAL; + + switch (iocommand.Request.Type.Direction) { + case XFER_NONE: + case XFER_WRITE: + case XFER_READ: + break; + default: + return -EINVAL; + } + + if (iocommand.buf_size > 0) { + kernel_buffer = kmalloc(iocommand.buf_size, GFP_KERNEL); + if (!kernel_buffer) + return -ENOMEM; + if (iocommand.Request.Type.Direction & XFER_WRITE) { + if (copy_from_user(kernel_buffer, iocommand.buf, + iocommand.buf_size)) { + rc = -EFAULT; + goto out; + } + } else { + memset(kernel_buffer, 0, iocommand.buf_size); + } + } + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO; + iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) - + PQI_REQUEST_HEADER_LENGTH; + memcpy(request.lun_number, iocommand.LUN_info.LunAddrBytes, + sizeof(request.lun_number)); + memcpy(request.cdb, iocommand.Request.CDB, iocommand.Request.CDBLen); + request.additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0; + + switch (iocommand.Request.Type.Direction) { + case XFER_NONE: + request.data_direction = SOP_NO_DIRECTION_FLAG; + break; + case XFER_WRITE: + request.data_direction = SOP_WRITE_FLAG; + break; + case XFER_READ: + request.data_direction = SOP_READ_FLAG; + break; + } + + request.task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + + if (iocommand.buf_size > 0) { + put_unaligned_le32(iocommand.buf_size, &request.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + &request.sg_descriptors[0], kernel_buffer, + iocommand.buf_size, PCI_DMA_BIDIRECTIONAL); + if (rc) + goto out; + + iu_length += sizeof(request.sg_descriptors[0]); + } + + put_unaligned_le16(iu_length, &request.header.iu_length); + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + PQI_SYNC_FLAGS_INTERRUPTABLE, &pqi_error_info, NO_TIMEOUT); + + if (iocommand.buf_size > 0) + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + PCI_DMA_BIDIRECTIONAL); + + memset(&iocommand.error_info, 0, sizeof(iocommand.error_info)); + + if (rc == 0) { + pqi_error_info_to_ciss(&pqi_error_info, &ciss_error_info); + iocommand.error_info.ScsiStatus = ciss_error_info.scsi_status; + iocommand.error_info.CommandStatus = + ciss_error_info.command_status; + sense_data_length = ciss_error_info.sense_data_length; + if (sense_data_length) { + if (sense_data_length > + sizeof(iocommand.error_info.SenseInfo)) + sense_data_length = + sizeof(iocommand.error_info.SenseInfo); + memcpy(iocommand.error_info.SenseInfo, + pqi_error_info.data, sense_data_length); + iocommand.error_info.SenseLen = sense_data_length; + } + } + + if (copy_to_user(arg, &iocommand, sizeof(iocommand))) { + rc = -EFAULT; + goto out; + } + + if (rc == 0 && iocommand.buf_size > 0 && + (iocommand.Request.Type.Direction & XFER_READ)) { + if (copy_to_user(iocommand.buf, kernel_buffer, + iocommand.buf_size)) { + rc = -EFAULT; + } + } + +out: + kfree(kernel_buffer); + + return rc; +} + +static int pqi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = shost_to_hba(sdev->host); + + switch (cmd) { + case CCISS_DEREGDISK: + case CCISS_REGNEWDISK: + case CCISS_REGNEWD: + rc = pqi_scan_scsi_devices(ctrl_info); + break; + case CCISS_GETPCIINFO: + rc = pqi_getpciinfo_ioctl(ctrl_info, arg); + break; + case CCISS_GETDRIVVER: + rc = pqi_getdrivver_ioctl(arg); + break; + case CCISS_PASSTHRU: + rc = pqi_passthru_ioctl(ctrl_info, arg); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static ssize_t pqi_version_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + struct Scsi_Host *shost; + struct pqi_ctrl_info *ctrl_info; + + shost = class_to_shost(dev); + ctrl_info = shost_to_hba(shost); + + count += snprintf(buffer + count, PAGE_SIZE - count, + " driver: %s\n", DRIVER_VERSION BUILD_TIMESTAMP); + + count += snprintf(buffer + count, PAGE_SIZE - count, + "firmware: %s\n", ctrl_info->firmware_version); + + return count; +} + +static ssize_t pqi_host_rescan_store(struct device *dev, + struct device_attribute *attr, const char *buffer, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + + pqi_scan_start(shost); + + return count; +} + +static DEVICE_ATTR(version, S_IRUGO, pqi_version_show, NULL); +static DEVICE_ATTR(rescan, S_IWUSR, NULL, pqi_host_rescan_store); + +static struct device_attribute *pqi_shost_attrs[] = { + &dev_attr_version, + &dev_attr_rescan, + NULL +}; + +static ssize_t pqi_sas_address_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + struct pqi_ctrl_info *ctrl_info; + struct scsi_device *sdev; + struct pqi_scsi_dev *device; + unsigned long flags; + u64 sas_address; + + sdev = to_scsi_device(dev); + ctrl_info = shost_to_hba(sdev->host); + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + device = sdev->hostdata; + if (pqi_is_logical_device(device)) { + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, + flags); + return -ENODEV; + } + sas_address = device->sas_address; + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + return snprintf(buffer, PAGE_SIZE, "0x%016llx\n", sas_address); +} + +static ssize_t pqi_ssd_smart_path_enabled_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + struct pqi_ctrl_info *ctrl_info; + struct scsi_device *sdev; + struct pqi_scsi_dev *device; + unsigned long flags; + + sdev = to_scsi_device(dev); + ctrl_info = shost_to_hba(sdev->host); + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + device = sdev->hostdata; + buffer[0] = device->offload_enabled ? '1' : '0'; + buffer[1] = '\n'; + buffer[2] = '\0'; + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + return 2; +} + +static DEVICE_ATTR(sas_address, S_IRUGO, pqi_sas_address_show, NULL); +static DEVICE_ATTR(ssd_smart_path_enabled, S_IRUGO, + pqi_ssd_smart_path_enabled_show, NULL); + +static struct device_attribute *pqi_sdev_attrs[] = { + &dev_attr_sas_address, + &dev_attr_ssd_smart_path_enabled, + NULL +}; + +static struct scsi_host_template pqi_driver_template = { + .module = THIS_MODULE, + .name = DRIVER_NAME_SHORT, + .proc_name = DRIVER_NAME_SHORT, + .queuecommand = pqi_scsi_queue_command, + .scan_start = pqi_scan_start, + .scan_finished = pqi_scan_finished, + .this_id = -1, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = pqi_eh_device_reset_handler, + .ioctl = pqi_ioctl, + .slave_alloc = pqi_slave_alloc, + .slave_configure = pqi_slave_configure, + .sdev_attrs = pqi_sdev_attrs, + .shost_attrs = pqi_shost_attrs, +}; + +static int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct Scsi_Host *shost; + + shost = scsi_host_alloc(&pqi_driver_template, sizeof(ctrl_info)); + if (!shost) { + dev_err(&ctrl_info->pci_dev->dev, + "scsi_host_alloc failed for controller %u\n", + ctrl_info->ctrl_id); + return -ENOMEM; + } + + shost->io_port = 0; + shost->n_io_port = 0; + shost->this_id = -1; + shost->max_channel = PQI_MAX_BUS; + shost->max_cmd_len = MAX_COMMAND_SIZE; + shost->max_lun = ~0; + shost->max_id = ~0; + shost->max_sectors = ctrl_info->max_sectors; + shost->can_queue = ctrl_info->scsi_ml_can_queue; + shost->cmd_per_lun = shost->can_queue; + shost->sg_tablesize = ctrl_info->sg_tablesize; + shost->transportt = pqi_sas_transport_template; + shost->irq = ctrl_info->msix_vectors[0]; + shost->unique_id = shost->irq; + shost->nr_hw_queues = ctrl_info->num_queue_groups; + shost->hostdata[0] = (unsigned long)ctrl_info; + + rc = scsi_add_host(shost, &ctrl_info->pci_dev->dev); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "scsi_add_host failed for controller %u\n", + ctrl_info->ctrl_id); + goto free_host; + } + + rc = pqi_add_sas_host(shost, ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "add SAS host failed for controller %u\n", + ctrl_info->ctrl_id); + goto remove_host; + } + + ctrl_info->scsi_host = shost; + + return 0; + +remove_host: + scsi_remove_host(shost); +free_host: + scsi_host_put(shost); + + return rc; +} + +static void pqi_unregister_scsi(struct pqi_ctrl_info *ctrl_info) +{ + struct Scsi_Host *shost; + + pqi_delete_sas_host(ctrl_info); + + shost = ctrl_info->scsi_host; + if (!shost) + return; + + scsi_remove_host(shost); + scsi_host_put(shost); +} + +#define PQI_RESET_ACTION_RESET 0x1 + +#define PQI_RESET_TYPE_NO_RESET 0x0 +#define PQI_RESET_TYPE_SOFT_RESET 0x1 +#define PQI_RESET_TYPE_FIRM_RESET 0x2 +#define PQI_RESET_TYPE_HARD_RESET 0x3 + +static int pqi_reset(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + u32 reset_params; + + reset_params = (PQI_RESET_ACTION_RESET << 5) | + PQI_RESET_TYPE_HARD_RESET; + + writel(reset_params, + &ctrl_info->pqi_registers->device_reset); + + rc = pqi_wait_for_pqi_mode_ready(ctrl_info); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "PQI reset failed\n"); + + return rc; +} + +static int pqi_get_ctrl_firmware_version(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct bmic_identify_controller *identify; + + identify = kmalloc(sizeof(*identify), GFP_KERNEL); + if (!identify) + return -ENOMEM; + + rc = pqi_identify_controller(ctrl_info, identify); + if (rc) + goto out; + + memcpy(ctrl_info->firmware_version, identify->firmware_version, + sizeof(identify->firmware_version)); + ctrl_info->firmware_version[sizeof(identify->firmware_version)] = '\0'; + snprintf(ctrl_info->firmware_version + + strlen(ctrl_info->firmware_version), + sizeof(ctrl_info->firmware_version), + "-%u", get_unaligned_le16(&identify->firmware_build_number)); + +out: + kfree(identify); + + return rc; +} + +static int pqi_ctrl_init(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + + /* + * When the controller comes out of reset, it is always running + * in legacy SIS mode. This is so that it can be compatible + * with legacy drivers shipped with OSes. So we have to talk + * to it using SIS commands at first. Once we are satisified + * that the controller supports PQI, we transition it into PQI + * mode. + */ + + /* + * Wait until the controller is ready to start accepting SIS + * commands. + */ + rc = sis_wait_for_ctrl_ready(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error initializing SIS interface\n"); + return rc; + } + + /* + * Get the controller properties. This allows us to determine + * whether or not it supports PQI mode. + */ + rc = sis_get_ctrl_properties(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error obtaining controller properties\n"); + return rc; + } + + rc = sis_get_pqi_capabilities(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error obtaining controller capabilities\n"); + return rc; + } + + if (ctrl_info->max_outstanding_requests > PQI_MAX_OUTSTANDING_REQUESTS) + ctrl_info->max_outstanding_requests = + PQI_MAX_OUTSTANDING_REQUESTS; + + pqi_calculate_io_resources(ctrl_info); + + rc = pqi_alloc_error_buffer(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate PQI error buffer\n"); + return rc; + } + + /* + * If the function we are about to call succeeds, the + * controller will transition from legacy SIS mode + * into PQI mode. + */ + rc = sis_init_base_struct_addr(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error initializing PQI mode\n"); + return rc; + } + + /* Wait for the controller to complete the SIS -> PQI transition. */ + rc = pqi_wait_for_pqi_mode_ready(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "transition to PQI mode failed\n"); + return rc; + } + + /* From here on, we are running in PQI mode. */ + ctrl_info->pqi_mode_enabled = true; + + rc = pqi_alloc_admin_queues(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error allocating admin queues\n"); + return rc; + } + + rc = pqi_create_admin_queues(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating admin queues\n"); + return rc; + } + + rc = pqi_report_device_capability(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "obtaining device capability failed\n"); + return rc; + } + + rc = pqi_validate_device_capability(ctrl_info); + if (rc) + return rc; + + pqi_calculate_queue_resources(ctrl_info); + + rc = pqi_enable_msix_interrupts(ctrl_info); + if (rc) + return rc; + + if (ctrl_info->num_msix_vectors_enabled < ctrl_info->num_queue_groups) { + ctrl_info->max_msix_vectors = + ctrl_info->num_msix_vectors_enabled; + pqi_calculate_queue_resources(ctrl_info); + } + + rc = pqi_alloc_io_resources(ctrl_info); + if (rc) + return rc; + + rc = pqi_alloc_operational_queues(ctrl_info); + if (rc) + return rc; + + pqi_init_operational_queues(ctrl_info); + + rc = pqi_request_irqs(ctrl_info); + if (rc) + return rc; + + pqi_irq_set_affinity_hint(ctrl_info); + + rc = pqi_create_queues(ctrl_info); + if (rc) + return rc; + + sis_enable_msix(ctrl_info); + + rc = pqi_configure_events(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error configuring events\n"); + return rc; + } + + pqi_start_heartbeat_timer(ctrl_info); + + ctrl_info->controller_online = true; + + /* Register with the SCSI subsystem. */ + rc = pqi_register_scsi(ctrl_info); + if (rc) + return rc; + + rc = pqi_get_ctrl_firmware_version(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error obtaining firmware version\n"); + return rc; + } + + rc = pqi_write_driver_version_to_host_wellness(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error updating host wellness\n"); + return rc; + } + + pqi_schedule_update_time_worker(ctrl_info); + + pqi_scan_scsi_devices(ctrl_info); + + return 0; +} + +static int pqi_pci_init(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + u64 mask; + + rc = pci_enable_device(ctrl_info->pci_dev); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to enable PCI device\n"); + return rc; + } + + if (sizeof(dma_addr_t) > 4) + mask = DMA_BIT_MASK(64); + else + mask = DMA_BIT_MASK(32); + + rc = dma_set_mask(&ctrl_info->pci_dev->dev, mask); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, "failed to set DMA mask\n"); + goto disable_device; + } + + rc = pci_request_regions(ctrl_info->pci_dev, DRIVER_NAME_SHORT); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to obtain PCI resources\n"); + goto disable_device; + } + + ctrl_info->iomem_base = ioremap_nocache(pci_resource_start( + ctrl_info->pci_dev, 0), + sizeof(struct pqi_ctrl_registers)); + if (!ctrl_info->iomem_base) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to map memory for controller registers\n"); + rc = -ENOMEM; + goto release_regions; + } + + ctrl_info->registers = ctrl_info->iomem_base; + ctrl_info->pqi_registers = &ctrl_info->registers->pqi_registers; + + /* Enable bus mastering. */ + pci_set_master(ctrl_info->pci_dev); + + pci_set_drvdata(ctrl_info->pci_dev, ctrl_info); + + return 0; + +release_regions: + pci_release_regions(ctrl_info->pci_dev); +disable_device: + pci_disable_device(ctrl_info->pci_dev); + + return rc; +} + +static void pqi_cleanup_pci_init(struct pqi_ctrl_info *ctrl_info) +{ + iounmap(ctrl_info->iomem_base); + pci_release_regions(ctrl_info->pci_dev); + pci_disable_device(ctrl_info->pci_dev); + pci_set_drvdata(ctrl_info->pci_dev, NULL); +} + +static struct pqi_ctrl_info *pqi_alloc_ctrl_info(int numa_node) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = kzalloc_node(sizeof(struct pqi_ctrl_info), + GFP_KERNEL, numa_node); + if (!ctrl_info) + return NULL; + + mutex_init(&ctrl_info->scan_mutex); + + INIT_LIST_HEAD(&ctrl_info->scsi_device_list); + spin_lock_init(&ctrl_info->scsi_device_list_lock); + + INIT_WORK(&ctrl_info->event_work, pqi_event_worker); + atomic_set(&ctrl_info->num_interrupts, 0); + + INIT_DELAYED_WORK(&ctrl_info->rescan_work, pqi_rescan_worker); + INIT_DELAYED_WORK(&ctrl_info->update_time_work, pqi_update_time_worker); + + sema_init(&ctrl_info->sync_request_sem, + PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS); + sema_init(&ctrl_info->lun_reset_sem, PQI_RESERVED_IO_SLOTS_LUN_RESET); + + ctrl_info->ctrl_id = atomic_inc_return(&pqi_controller_count) - 1; + ctrl_info->max_msix_vectors = PQI_MAX_MSIX_VECTORS; + + return ctrl_info; +} + +static inline void pqi_free_ctrl_info(struct pqi_ctrl_info *ctrl_info) +{ + kfree(ctrl_info); +} + +static void pqi_free_interrupts(struct pqi_ctrl_info *ctrl_info) +{ + pqi_irq_unset_affinity_hint(ctrl_info); + pqi_free_irqs(ctrl_info); + if (ctrl_info->num_msix_vectors_enabled) + pci_disable_msix(ctrl_info->pci_dev); +} + +static void pqi_free_ctrl_resources(struct pqi_ctrl_info *ctrl_info) +{ + pqi_stop_heartbeat_timer(ctrl_info); + pqi_free_interrupts(ctrl_info); + if (ctrl_info->queue_memory_base) + dma_free_coherent(&ctrl_info->pci_dev->dev, + ctrl_info->queue_memory_length, + ctrl_info->queue_memory_base, + ctrl_info->queue_memory_base_dma_handle); + if (ctrl_info->admin_queue_memory_base) + dma_free_coherent(&ctrl_info->pci_dev->dev, + ctrl_info->admin_queue_memory_length, + ctrl_info->admin_queue_memory_base, + ctrl_info->admin_queue_memory_base_dma_handle); + pqi_free_all_io_requests(ctrl_info); + if (ctrl_info->error_buffer) + dma_free_coherent(&ctrl_info->pci_dev->dev, + ctrl_info->error_buffer_length, + ctrl_info->error_buffer, + ctrl_info->error_buffer_dma_handle); + if (ctrl_info->iomem_base) + pqi_cleanup_pci_init(ctrl_info); + pqi_free_ctrl_info(ctrl_info); +} + +static void pqi_remove_ctrl(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + + if (ctrl_info->controller_online) { + cancel_delayed_work_sync(&ctrl_info->rescan_work); + cancel_delayed_work_sync(&ctrl_info->update_time_work); + pqi_remove_all_scsi_devices(ctrl_info); + pqi_unregister_scsi(ctrl_info); + ctrl_info->controller_online = false; + } + if (ctrl_info->pqi_mode_enabled) { + sis_disable_msix(ctrl_info); + rc = pqi_reset(ctrl_info); + if (rc == 0) + sis_reenable_sis_mode(ctrl_info); + } + pqi_free_ctrl_resources(ctrl_info); +} + +static void pqi_print_ctrl_info(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + char *ctrl_description; + + if (id->driver_data) { + ctrl_description = (char *)id->driver_data; + } else { + switch (id->subvendor) { + case PCI_VENDOR_ID_HP: + ctrl_description = hpe_branded_controller; + break; + case PCI_VENDOR_ID_ADAPTEC2: + default: + ctrl_description = microsemi_branded_controller; + break; + } + } + + dev_info(&pdev->dev, "%s found\n", ctrl_description); +} + +static int pqi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int rc; + int node; + struct pqi_ctrl_info *ctrl_info; + + pqi_print_ctrl_info(pdev, id); + + if (pqi_disable_device_id_wildcards && + id->subvendor == PCI_ANY_ID && + id->subdevice == PCI_ANY_ID) { + dev_warn(&pdev->dev, + "controller not probed because device ID wildcards are disabled\n"); + return -ENODEV; + } + + if (id->subvendor == PCI_ANY_ID || id->subdevice == PCI_ANY_ID) + dev_warn(&pdev->dev, + "controller device ID matched using wildcards\n"); + + node = dev_to_node(&pdev->dev); + if (node == NUMA_NO_NODE) + set_dev_node(&pdev->dev, 0); + + ctrl_info = pqi_alloc_ctrl_info(node); + if (!ctrl_info) { + dev_err(&pdev->dev, + "failed to allocate controller info block\n"); + return -ENOMEM; + } + + ctrl_info->pci_dev = pdev; + + rc = pqi_pci_init(ctrl_info); + if (rc) + goto error; + + rc = pqi_ctrl_init(ctrl_info); + if (rc) + goto error; + + return 0; + +error: + pqi_remove_ctrl(ctrl_info); + + return rc; +} + +static void pqi_pci_remove(struct pci_dev *pdev) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = pci_get_drvdata(pdev); + if (!ctrl_info) + return; + + pqi_remove_ctrl(ctrl_info); +} + +static void pqi_shutdown(struct pci_dev *pdev) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = pci_get_drvdata(pdev); + if (!ctrl_info) + goto error; + + /* + * Write all data in the controller's battery-backed cache to + * storage. + */ + rc = pqi_flush_cache(ctrl_info); + if (rc == 0) + return; + +error: + dev_warn(&pdev->dev, + "unable to flush controller cache\n"); +} + +/* Define the PCI IDs for the controllers that we support. */ +static const struct pci_device_id pqi_pci_id_table[] = { + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0110) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0600) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0601) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0602) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0603) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0650) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0651) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0652) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0653) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0654) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0655) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0700) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0701) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0800) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0801) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0802) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0803) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0804) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0805) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0900) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0901) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0902) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0903) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0904) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0905) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0906) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1001) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1100) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1101) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1102) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1150) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_ANY_ID, PCI_ANY_ID) + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, pqi_pci_id_table); + +static struct pci_driver pqi_pci_driver = { + .name = DRIVER_NAME_SHORT, + .id_table = pqi_pci_id_table, + .probe = pqi_pci_probe, + .remove = pqi_pci_remove, + .shutdown = pqi_shutdown, +}; + +static int __init pqi_init(void) +{ + int rc; + + pr_info(DRIVER_NAME "\n"); + + pqi_sas_transport_template = + sas_attach_transport(&pqi_sas_transport_functions); + if (!pqi_sas_transport_template) + return -ENODEV; + + rc = pci_register_driver(&pqi_pci_driver); + if (rc) + sas_release_transport(pqi_sas_transport_template); + + return rc; +} + +static void __exit pqi_cleanup(void) +{ + pci_unregister_driver(&pqi_pci_driver); + sas_release_transport(pqi_sas_transport_template); +} + +module_init(pqi_init); +module_exit(pqi_cleanup); + +static void __attribute__((unused)) verify_structures(void) +{ + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_host_to_ctrl_doorbell) != 0x20); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_interrupt_mask) != 0x34); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_ctrl_to_host_doorbell) != 0x9c); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_ctrl_to_host_doorbell_clear) != 0xa0); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_firmware_status) != 0xbc); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_mailbox) != 0x1000); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + pqi_registers) != 0x4000); + + BUILD_BUG_ON(offsetof(struct pqi_iu_header, + iu_type) != 0x0); + BUILD_BUG_ON(offsetof(struct pqi_iu_header, + iu_length) != 0x2); + BUILD_BUG_ON(offsetof(struct pqi_iu_header, + response_queue_id) != 0x4); + BUILD_BUG_ON(offsetof(struct pqi_iu_header, + work_area) != 0x6); + BUILD_BUG_ON(sizeof(struct pqi_iu_header) != 0x8); + + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + status) != 0x0); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + service_response) != 0x1); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + data_present) != 0x2); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + reserved) != 0x3); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + residual_count) != 0x4); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + data_length) != 0x8); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + reserved1) != 0xa); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + data) != 0xc); + BUILD_BUG_ON(sizeof(struct pqi_aio_error_info) != 0x10c); + + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data_in_result) != 0x0); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data_out_result) != 0x1); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + reserved) != 0x2); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + status) != 0x5); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + status_qualifier) != 0x6); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + sense_data_length) != 0x8); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + response_data_length) != 0xa); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data_in_transferred) != 0xc); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data_out_transferred) != 0x10); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data) != 0x14); + BUILD_BUG_ON(sizeof(struct pqi_raid_error_info) != 0x114); + + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + signature) != 0x0); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + function_and_status_code) != 0x8); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + max_admin_iq_elements) != 0x10); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + max_admin_oq_elements) != 0x11); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_element_length) != 0x12); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_element_length) != 0x13); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + max_reset_timeout) != 0x14); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + legacy_intx_status) != 0x18); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + legacy_intx_mask_set) != 0x1c); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + legacy_intx_mask_clear) != 0x20); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + device_status) != 0x40); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_pi_offset) != 0x48); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_ci_offset) != 0x50); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_element_array_addr) != 0x58); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_element_array_addr) != 0x60); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_ci_addr) != 0x68); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_pi_addr) != 0x70); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_num_elements) != 0x78); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_num_elements) != 0x79); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_queue_int_msg_num) != 0x7a); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + device_error) != 0x80); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + error_details) != 0x88); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + device_reset) != 0x90); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + power_action) != 0x94); + BUILD_BUG_ON(sizeof(struct pqi_device_registers) != 0x100); + + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + header.work_area) != 6); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + function_code) != 10); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.report_device_capability.buffer_length) != 44); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.report_device_capability.sg_descriptor) != 48); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.queue_id) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.element_array_addr) != 16); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.ci_addr) != 24); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.num_elements) != 32); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.element_length) != 34); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.queue_protocol) != 36); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.queue_id) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.element_array_addr) != 16); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.pi_addr) != 24); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.num_elements) != 32); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.element_length) != 34); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.queue_protocol) != 36); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.int_msg_num) != 40); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.coalescing_count) != 42); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.min_coalescing_time) != 44); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.max_coalescing_time) != 48); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.delete_operational_queue.queue_id) != 12); + BUILD_BUG_ON(sizeof(struct pqi_general_admin_request) != 64); + BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + data.create_operational_iq) != 64 - 11); + BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + data.create_operational_oq) != 64 - 11); + BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + data.delete_operational_queue) != 64 - 11); + + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + header.work_area) != 6); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + function_code) != 10); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + status) != 11); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + data.create_operational_iq.status_descriptor) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + data.create_operational_iq.iq_pi_offset) != 16); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + data.create_operational_oq.status_descriptor) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + data.create_operational_oq.oq_ci_offset) != 16); + BUILD_BUG_ON(sizeof(struct pqi_general_admin_response) != 64); + + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + header.response_queue_id) != 4); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + header.work_area) != 6); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + nexus_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + buffer_length) != 12); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + lun_number) != 16); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + protocol_specific) != 24); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + error_index) != 27); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + cdb) != 32); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + sg_descriptors) != 64); + BUILD_BUG_ON(sizeof(struct pqi_raid_path_request) != + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + header.response_queue_id) != 4); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + header.work_area) != 6); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + nexus_id) != 12); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + buffer_length) != 16); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + data_encryption_key_index) != 22); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + encrypt_tweak_lower) != 24); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + encrypt_tweak_upper) != 28); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + cdb) != 32); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + error_index) != 48); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + num_sg_descriptors) != 50); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + cdb_length) != 51); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + lun_number) != 52); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + sg_descriptors) != 64); + BUILD_BUG_ON(sizeof(struct pqi_aio_path_request) != + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + BUILD_BUG_ON(offsetof(struct pqi_io_response, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_io_response, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_io_response, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_io_response, + error_index) != 10); + + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + header.response_queue_id) != 4); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.report_event_configuration.buffer_length) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.report_event_configuration.sg_descriptors) != 16); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.set_event_configuration.global_event_oq_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.set_event_configuration.buffer_length) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.set_event_configuration.sg_descriptors) != 16); + + BUILD_BUG_ON(offsetof(struct pqi_iu_layer_descriptor, + max_inbound_iu_length) != 6); + BUILD_BUG_ON(offsetof(struct pqi_iu_layer_descriptor, + max_outbound_iu_length) != 14); + BUILD_BUG_ON(sizeof(struct pqi_iu_layer_descriptor) != 16); + + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + data_length) != 0); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + iq_arbitration_priority_support_bitmask) != 8); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + maximum_aw_a) != 9); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + maximum_aw_b) != 10); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + maximum_aw_c) != 11); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_inbound_queues) != 16); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_elements_per_iq) != 18); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_iq_element_length) != 24); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + min_iq_element_length) != 26); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_outbound_queues) != 30); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_elements_per_oq) != 32); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + intr_coalescing_time_granularity) != 34); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_oq_element_length) != 36); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + min_oq_element_length) != 38); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + iu_layer_descriptors) != 64); + BUILD_BUG_ON(sizeof(struct pqi_device_capability) != 576); + + BUILD_BUG_ON(offsetof(struct pqi_event_descriptor, + event_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_event_descriptor, + oq_id) != 2); + BUILD_BUG_ON(sizeof(struct pqi_event_descriptor) != 4); + + BUILD_BUG_ON(offsetof(struct pqi_event_config, + num_event_descriptors) != 2); + BUILD_BUG_ON(offsetof(struct pqi_event_config, + descriptors) != 4); + + BUILD_BUG_ON(offsetof(struct pqi_event_response, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + event_type) != 8); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + event_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + additional_event_id) != 12); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + data) != 16); + BUILD_BUG_ON(sizeof(struct pqi_event_response) != 32); + + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + event_type) != 8); + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + event_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + additional_event_id) != 12); + BUILD_BUG_ON(sizeof(struct pqi_event_acknowledge_request) != 16); + + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + nexus_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + lun_number) != 16); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + protocol_specific) != 24); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + outbound_queue_id_to_manage) != 26); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + request_id_to_manage) != 28); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + task_management_function) != 30); + BUILD_BUG_ON(sizeof(struct pqi_task_management_request) != 32); + + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + nexus_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + additional_response_info) != 12); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + response_code) != 15); + BUILD_BUG_ON(sizeof(struct pqi_task_management_response) != 16); + + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + configured_logical_drive_count) != 0); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + configuration_signature) != 1); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + firmware_version) != 5); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + extended_logical_unit_count) != 154); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + firmware_build_number) != 190); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + controller_mode) != 292); + + BUILD_BUG_ON(PQI_ADMIN_IQ_NUM_ELEMENTS > 255); + BUILD_BUG_ON(PQI_ADMIN_OQ_NUM_ELEMENTS > 255); + BUILD_BUG_ON(PQI_ADMIN_IQ_ELEMENT_LENGTH % + PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0); + BUILD_BUG_ON(PQI_ADMIN_OQ_ELEMENT_LENGTH % + PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0); + BUILD_BUG_ON(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH > 1048560); + BUILD_BUG_ON(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH % + PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0); + BUILD_BUG_ON(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH > 1048560); + BUILD_BUG_ON(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH % + PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0); + + BUILD_BUG_ON(PQI_RESERVED_IO_SLOTS >= PQI_MAX_OUTSTANDING_REQUESTS); +} diff --git a/drivers/scsi/smartpqi/smartpqi_sas_transport.c b/drivers/scsi/smartpqi/smartpqi_sas_transport.c new file mode 100644 index 000000000000..52ca4f93f1b2 --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi_sas_transport.c @@ -0,0 +1,350 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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; version 2 of the License. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#include +#include +#include +#include +#include "smartpqi.h" + +static struct pqi_sas_phy *pqi_alloc_sas_phy(struct pqi_sas_port *pqi_sas_port) +{ + struct pqi_sas_phy *pqi_sas_phy; + struct sas_phy *phy; + + pqi_sas_phy = kzalloc(sizeof(*pqi_sas_phy), GFP_KERNEL); + if (!pqi_sas_phy) + return NULL; + + phy = sas_phy_alloc(pqi_sas_port->parent_node->parent_dev, + pqi_sas_port->next_phy_index); + if (!phy) { + kfree(pqi_sas_phy); + return NULL; + } + + pqi_sas_port->next_phy_index++; + pqi_sas_phy->phy = phy; + pqi_sas_phy->parent_port = pqi_sas_port; + + return pqi_sas_phy; +} + +static void pqi_free_sas_phy(struct pqi_sas_phy *pqi_sas_phy) +{ + struct sas_phy *phy = pqi_sas_phy->phy; + + sas_port_delete_phy(pqi_sas_phy->parent_port->port, phy); + sas_phy_free(phy); + if (pqi_sas_phy->added_to_port) + list_del(&pqi_sas_phy->phy_list_entry); + kfree(pqi_sas_phy); +} + +static int pqi_sas_port_add_phy(struct pqi_sas_phy *pqi_sas_phy) +{ + int rc; + struct pqi_sas_port *pqi_sas_port; + struct sas_phy *phy; + struct sas_identify *identify; + + pqi_sas_port = pqi_sas_phy->parent_port; + phy = pqi_sas_phy->phy; + + identify = &phy->identify; + memset(identify, 0, sizeof(*identify)); + identify->sas_address = pqi_sas_port->sas_address; + identify->device_type = SAS_END_DEVICE; + identify->initiator_port_protocols = SAS_PROTOCOL_STP; + identify->target_port_protocols = SAS_PROTOCOL_STP; + phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; + phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; + phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; + phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; + phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; + + rc = sas_phy_add(pqi_sas_phy->phy); + if (rc) + return rc; + + sas_port_add_phy(pqi_sas_port->port, pqi_sas_phy->phy); + list_add_tail(&pqi_sas_phy->phy_list_entry, + &pqi_sas_port->phy_list_head); + pqi_sas_phy->added_to_port = true; + + return 0; +} + +static int pqi_sas_port_add_rphy(struct pqi_sas_port *pqi_sas_port, + struct sas_rphy *rphy) +{ + struct sas_identify *identify; + + identify = &rphy->identify; + identify->sas_address = pqi_sas_port->sas_address; + identify->initiator_port_protocols = SAS_PROTOCOL_STP; + identify->target_port_protocols = SAS_PROTOCOL_STP; + + return sas_rphy_add(rphy); +} + +static struct pqi_sas_port *pqi_alloc_sas_port( + struct pqi_sas_node *pqi_sas_node, u64 sas_address) +{ + int rc; + struct pqi_sas_port *pqi_sas_port; + struct sas_port *port; + + pqi_sas_port = kzalloc(sizeof(*pqi_sas_port), GFP_KERNEL); + if (!pqi_sas_port) + return NULL; + + INIT_LIST_HEAD(&pqi_sas_port->phy_list_head); + pqi_sas_port->parent_node = pqi_sas_node; + + port = sas_port_alloc_num(pqi_sas_node->parent_dev); + if (!port) + goto free_pqi_port; + + rc = sas_port_add(port); + if (rc) + goto free_sas_port; + + pqi_sas_port->port = port; + pqi_sas_port->sas_address = sas_address; + list_add_tail(&pqi_sas_port->port_list_entry, + &pqi_sas_node->port_list_head); + + return pqi_sas_port; + +free_sas_port: + sas_port_free(port); +free_pqi_port: + kfree(pqi_sas_port); + + return NULL; +} + +static void pqi_free_sas_port(struct pqi_sas_port *pqi_sas_port) +{ + struct pqi_sas_phy *pqi_sas_phy; + struct pqi_sas_phy *next; + + list_for_each_entry_safe(pqi_sas_phy, next, + &pqi_sas_port->phy_list_head, phy_list_entry) + pqi_free_sas_phy(pqi_sas_phy); + + sas_port_delete(pqi_sas_port->port); + list_del(&pqi_sas_port->port_list_entry); + kfree(pqi_sas_port); +} + +static struct pqi_sas_node *pqi_alloc_sas_node(struct device *parent_dev) +{ + struct pqi_sas_node *pqi_sas_node; + + pqi_sas_node = kzalloc(sizeof(*pqi_sas_node), GFP_KERNEL); + if (pqi_sas_node) { + pqi_sas_node->parent_dev = parent_dev; + INIT_LIST_HEAD(&pqi_sas_node->port_list_head); + } + + return pqi_sas_node; +} + +static void pqi_free_sas_node(struct pqi_sas_node *pqi_sas_node) +{ + struct pqi_sas_port *pqi_sas_port; + struct pqi_sas_port *next; + + if (!pqi_sas_node) + return; + + list_for_each_entry_safe(pqi_sas_port, next, + &pqi_sas_node->port_list_head, port_list_entry) + pqi_free_sas_port(pqi_sas_port); + + kfree(pqi_sas_node); +} + +struct pqi_scsi_dev *pqi_find_device_by_sas_rphy( + struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (!device->sas_port) + continue; + if (device->sas_port->rphy == rphy) + return device; + } + + return NULL; +} + +int pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct device *parent_dev; + struct pqi_sas_node *pqi_sas_node; + struct pqi_sas_port *pqi_sas_port; + struct pqi_sas_phy *pqi_sas_phy; + + parent_dev = &shost->shost_gendev; + + pqi_sas_node = pqi_alloc_sas_node(parent_dev); + if (!pqi_sas_node) + return -ENOMEM; + + pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, ctrl_info->sas_address); + if (!pqi_sas_port) { + rc = -ENODEV; + goto free_sas_node; + } + + pqi_sas_phy = pqi_alloc_sas_phy(pqi_sas_port); + if (!pqi_sas_phy) { + rc = -ENODEV; + goto free_sas_port; + } + + rc = pqi_sas_port_add_phy(pqi_sas_phy); + if (rc) + goto free_sas_phy; + + ctrl_info->sas_host = pqi_sas_node; + + return 0; + +free_sas_phy: + pqi_free_sas_phy(pqi_sas_phy); +free_sas_port: + pqi_free_sas_port(pqi_sas_port); +free_sas_node: + pqi_free_sas_node(pqi_sas_node); + + return rc; +} + +void pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info) +{ + pqi_free_sas_node(ctrl_info->sas_host); +} + +int pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node, + struct pqi_scsi_dev *device) +{ + int rc; + struct pqi_sas_port *pqi_sas_port; + struct sas_rphy *rphy; + + pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, device->sas_address); + if (!pqi_sas_port) + return -ENOMEM; + + rphy = sas_end_device_alloc(pqi_sas_port->port); + if (!rphy) { + rc = -ENODEV; + goto free_sas_port; + } + + pqi_sas_port->rphy = rphy; + device->sas_port = pqi_sas_port; + + rc = pqi_sas_port_add_rphy(pqi_sas_port, rphy); + if (rc) + goto free_sas_port; + + return 0; + +free_sas_port: + pqi_free_sas_port(pqi_sas_port); + device->sas_port = NULL; + + return rc; +} + +void pqi_remove_sas_device(struct pqi_scsi_dev *device) +{ + if (device->sas_port) { + pqi_free_sas_port(device->sas_port); + device->sas_port = NULL; + } +} + +static int pqi_sas_get_linkerrors(struct sas_phy *phy) +{ + return 0; +} + +static int pqi_sas_get_enclosure_identifier(struct sas_rphy *rphy, + u64 *identifier) +{ + return 0; +} + +static int pqi_sas_get_bay_identifier(struct sas_rphy *rphy) +{ + return -ENXIO; +} + +static int pqi_sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ + return 0; +} + +static int pqi_sas_phy_enable(struct sas_phy *phy, int enable) +{ + return 0; +} + +static int pqi_sas_phy_setup(struct sas_phy *phy) +{ + return 0; +} + +static void pqi_sas_phy_release(struct sas_phy *phy) +{ +} + +static int pqi_sas_phy_speed(struct sas_phy *phy, + struct sas_phy_linkrates *rates) +{ + return -EINVAL; +} + +/* SMP = Serial Management Protocol */ + +static int pqi_sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req) +{ + return -EINVAL; +} + +struct sas_function_template pqi_sas_transport_functions = { + .get_linkerrors = pqi_sas_get_linkerrors, + .get_enclosure_identifier = pqi_sas_get_enclosure_identifier, + .get_bay_identifier = pqi_sas_get_bay_identifier, + .phy_reset = pqi_sas_phy_reset, + .phy_enable = pqi_sas_phy_enable, + .phy_setup = pqi_sas_phy_setup, + .phy_release = pqi_sas_phy_release, + .set_phy_speed = pqi_sas_phy_speed, + .smp_handler = pqi_sas_smp_handler, +}; diff --git a/drivers/scsi/smartpqi/smartpqi_sis.c b/drivers/scsi/smartpqi/smartpqi_sis.c new file mode 100644 index 000000000000..5d416a8aa462 --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi_sis.c @@ -0,0 +1,394 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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; version 2 of the License. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#include +#include +#include +#include +#include +#include +#include "smartpqi.h" +#include "smartpqi_sis.h" + +/* legacy SIS interface commands */ +#define SIS_CMD_GET_ADAPTER_PROPERTIES 0x19 +#define SIS_CMD_INIT_BASE_STRUCT_ADDRESS 0x1b +#define SIS_CMD_GET_PQI_CAPABILITIES 0x3000 + +/* for submission of legacy SIS commands */ +#define SIS_REENABLE_SIS_MODE 0x1 +#define SIS_ENABLE_MSIX 0x40 +#define SIS_SOFT_RESET 0x100 +#define SIS_CMD_READY 0x200 +#define SIS_CMD_COMPLETE 0x1000 +#define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000 +#define SIS_CMD_STATUS_SUCCESS 0x1 +#define SIS_CMD_COMPLETE_TIMEOUT_SECS 30 +#define SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS 10 + +/* used with SIS_CMD_GET_ADAPTER_PROPERTIES command */ +#define SIS_EXTENDED_PROPERTIES_SUPPORTED 0x800000 +#define SIS_SMARTARRAY_FEATURES_SUPPORTED 0x2 +#define SIS_PQI_MODE_SUPPORTED 0x4 +#define SIS_REQUIRED_EXTENDED_PROPERTIES \ + (SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED) + +/* used with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */ +#define SIS_BASE_STRUCT_REVISION 9 +#define SIS_BASE_STRUCT_ALIGNMENT 16 + +#define SIS_CTRL_KERNEL_UP 0x80 +#define SIS_CTRL_KERNEL_PANIC 0x100 +#define SIS_CTRL_READY_TIMEOUT_SECS 30 +#define SIS_CTRL_READY_POLL_INTERVAL_MSECS 10 + +#pragma pack(1) + +/* for use with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */ +struct sis_base_struct { + __le32 revision; /* revision of this structure */ + __le32 flags; /* reserved */ + __le32 error_buffer_paddr_low; /* lower 32 bits of physical memory */ + /* buffer for PQI error response */ + /* data */ + __le32 error_buffer_paddr_high; /* upper 32 bits of physical */ + /* memory buffer for PQI */ + /* error response data */ + __le32 error_buffer_element_length; /* length of each PQI error */ + /* response buffer element */ + /* in bytes */ + __le32 error_buffer_num_elements; /* total number of PQI error */ + /* response buffers available */ +}; + +#pragma pack() + +int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info) +{ + unsigned long timeout; + u32 status; + + timeout = (SIS_CTRL_READY_TIMEOUT_SECS * HZ) + jiffies; + + while (1) { + status = readl(&ctrl_info->registers->sis_firmware_status); + if (status != ~0) { + if (status & SIS_CTRL_KERNEL_PANIC) { + dev_err(&ctrl_info->pci_dev->dev, + "controller is offline: status code 0x%x\n", + readl( + &ctrl_info->registers->sis_mailbox[7])); + return -ENODEV; + } + if (status & SIS_CTRL_KERNEL_UP) + break; + } + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + msleep(SIS_CTRL_READY_POLL_INTERVAL_MSECS); + } + + return 0; +} + +bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info) +{ + bool running; + u32 status; + + status = readl(&ctrl_info->registers->sis_firmware_status); + + if (status & SIS_CTRL_KERNEL_PANIC) + running = false; + else + running = true; + + if (!running) + dev_err(&ctrl_info->pci_dev->dev, + "controller is offline: status code 0x%x\n", + readl(&ctrl_info->registers->sis_mailbox[7])); + + return running; +} + +/* used for passing command parameters/results when issuing SIS commands */ +struct sis_sync_cmd_params { + u32 mailbox[6]; /* mailboxes 0-5 */ +}; + +static int sis_send_sync_cmd(struct pqi_ctrl_info *ctrl_info, + u32 cmd, struct sis_sync_cmd_params *params) +{ + struct pqi_ctrl_registers __iomem *registers; + unsigned int i; + unsigned long timeout; + u32 doorbell; + u32 cmd_status; + + registers = ctrl_info->registers; + + /* Write the command to mailbox 0. */ + writel(cmd, ®isters->sis_mailbox[0]); + + /* + * Write the command parameters to mailboxes 1-4 (mailbox 5 is not used + * when sending a command to the controller). + */ + for (i = 1; i <= 4; i++) + writel(params->mailbox[i], ®isters->sis_mailbox[i]); + + /* Clear the command doorbell. */ + writel(SIS_CLEAR_CTRL_TO_HOST_DOORBELL, + ®isters->sis_ctrl_to_host_doorbell_clear); + + /* Disable doorbell interrupts by masking all interrupts. */ + writel(~0, ®isters->sis_interrupt_mask); + + /* + * Force the completion of the interrupt mask register write before + * submitting the command. + */ + readl(®isters->sis_interrupt_mask); + + /* Submit the command to the controller. */ + writel(SIS_CMD_READY, ®isters->sis_host_to_ctrl_doorbell); + + /* + * Poll for command completion. Note that the call to msleep() is at + * the top of the loop in order to give the controller time to start + * processing the command before we start polling. + */ + timeout = (SIS_CMD_COMPLETE_TIMEOUT_SECS * HZ) + jiffies; + while (1) { + msleep(SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS); + doorbell = readl(®isters->sis_ctrl_to_host_doorbell); + if (doorbell & SIS_CMD_COMPLETE) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + + /* Read the command status from mailbox 0. */ + cmd_status = readl(®isters->sis_mailbox[0]); + if (cmd_status != SIS_CMD_STATUS_SUCCESS) { + dev_err(&ctrl_info->pci_dev->dev, + "SIS command failed for command 0x%x: status = 0x%x\n", + cmd, cmd_status); + return -EINVAL; + } + + /* + * The command completed successfully, so save the command status and + * read the values returned in mailboxes 1-5. + */ + params->mailbox[0] = cmd_status; + for (i = 1; i < ARRAY_SIZE(params->mailbox); i++) + params->mailbox[i] = readl(®isters->sis_mailbox[i]); + + return 0; +} + +/* + * This function verifies that we are talking to a controller that speaks PQI. + */ + +int sis_get_ctrl_properties(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + u32 properties; + u32 extended_properties; + struct sis_sync_cmd_params params; + + memset(¶ms, 0, sizeof(params)); + + rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_ADAPTER_PROPERTIES, + ¶ms); + if (rc) + return rc; + + properties = params.mailbox[1]; + + if (!(properties & SIS_EXTENDED_PROPERTIES_SUPPORTED)) + return -ENODEV; + + extended_properties = params.mailbox[4]; + + if ((extended_properties & SIS_REQUIRED_EXTENDED_PROPERTIES) != + SIS_REQUIRED_EXTENDED_PROPERTIES) + return -ENODEV; + + return 0; +} + +int sis_get_pqi_capabilities(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct sis_sync_cmd_params params; + + memset(¶ms, 0, sizeof(params)); + + rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_PQI_CAPABILITIES, + ¶ms); + if (rc) + return rc; + + ctrl_info->max_sg_entries = params.mailbox[1]; + ctrl_info->max_transfer_size = params.mailbox[2]; + ctrl_info->max_outstanding_requests = params.mailbox[3]; + ctrl_info->config_table_offset = params.mailbox[4]; + ctrl_info->config_table_length = params.mailbox[5]; + + return 0; +} + +int sis_init_base_struct_addr(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + void *base_struct_unaligned; + struct sis_base_struct *base_struct; + struct sis_sync_cmd_params params; + unsigned long error_buffer_paddr; + dma_addr_t bus_address; + + base_struct_unaligned = kzalloc(sizeof(*base_struct) + + SIS_BASE_STRUCT_ALIGNMENT - 1, GFP_KERNEL); + if (!base_struct_unaligned) + return -ENOMEM; + + base_struct = PTR_ALIGN(base_struct_unaligned, + SIS_BASE_STRUCT_ALIGNMENT); + error_buffer_paddr = (unsigned long)ctrl_info->error_buffer_dma_handle; + + put_unaligned_le32(SIS_BASE_STRUCT_REVISION, &base_struct->revision); + put_unaligned_le32(lower_32_bits(error_buffer_paddr), + &base_struct->error_buffer_paddr_low); + put_unaligned_le32(upper_32_bits(error_buffer_paddr), + &base_struct->error_buffer_paddr_high); + put_unaligned_le32(PQI_ERROR_BUFFER_ELEMENT_LENGTH, + &base_struct->error_buffer_element_length); + put_unaligned_le32(ctrl_info->max_io_slots, + &base_struct->error_buffer_num_elements); + + bus_address = pci_map_single(ctrl_info->pci_dev, base_struct, + sizeof(*base_struct), PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(ctrl_info->pci_dev, bus_address)) { + rc = -ENOMEM; + goto out; + } + + memset(¶ms, 0, sizeof(params)); + params.mailbox[1] = lower_32_bits((u64)bus_address); + params.mailbox[2] = upper_32_bits((u64)bus_address); + params.mailbox[3] = sizeof(*base_struct); + + rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_INIT_BASE_STRUCT_ADDRESS, + ¶ms); + + pci_unmap_single(ctrl_info->pci_dev, bus_address, sizeof(*base_struct), + PCI_DMA_TODEVICE); + +out: + kfree(base_struct_unaligned); + + return rc; +} + +/* Enable MSI-X interrupts on the controller. */ + +void sis_enable_msix(struct pqi_ctrl_info *ctrl_info) +{ + u32 doorbell_register; + + doorbell_register = + readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); + doorbell_register |= SIS_ENABLE_MSIX; + + writel(doorbell_register, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); +} + +/* Disable MSI-X interrupts on the controller. */ + +void sis_disable_msix(struct pqi_ctrl_info *ctrl_info) +{ + u32 doorbell_register; + + doorbell_register = + readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); + doorbell_register &= ~SIS_ENABLE_MSIX; + + writel(doorbell_register, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); +} + +void sis_soft_reset(struct pqi_ctrl_info *ctrl_info) +{ + writel(SIS_SOFT_RESET, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); +} + +#define SIS_MODE_READY_TIMEOUT_SECS 30 + +int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + unsigned long timeout; + struct pqi_ctrl_registers __iomem *registers; + u32 doorbell; + + registers = ctrl_info->registers; + + writel(SIS_REENABLE_SIS_MODE, + ®isters->sis_host_to_ctrl_doorbell); + + rc = 0; + timeout = (SIS_MODE_READY_TIMEOUT_SECS * HZ) + jiffies; + + while (1) { + doorbell = readl(®isters->sis_ctrl_to_host_doorbell); + if ((doorbell & SIS_REENABLE_SIS_MODE) == 0) + break; + if (time_after(jiffies, timeout)) { + rc = -ETIMEDOUT; + break; + } + } + + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "re-enabling SIS mode failed\n"); + + return rc; +} + +static void __attribute__((unused)) verify_structures(void) +{ + BUILD_BUG_ON(offsetof(struct sis_base_struct, + revision) != 0x0); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + flags) != 0x4); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + error_buffer_paddr_low) != 0x8); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + error_buffer_paddr_high) != 0xc); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + error_buffer_element_length) != 0x10); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + error_buffer_num_elements) != 0x14); + BUILD_BUG_ON(sizeof(struct sis_base_struct) != 0x18); +} diff --git a/drivers/scsi/smartpqi/smartpqi_sis.h b/drivers/scsi/smartpqi/smartpqi_sis.h new file mode 100644 index 000000000000..d2ff8d54754b --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi_sis.h @@ -0,0 +1,32 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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; version 2 of the License. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#if !defined(_SMARTPQI_SIS_H) +#define _SMARTPQI_SIS_H + +int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info); +bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info); +int sis_get_ctrl_properties(struct pqi_ctrl_info *ctrl_info); +int sis_get_pqi_capabilities(struct pqi_ctrl_info *ctrl_info); +int sis_init_base_struct_addr(struct pqi_ctrl_info *ctrl_info); +void sis_enable_msix(struct pqi_ctrl_info *ctrl_info); +void sis_disable_msix(struct pqi_ctrl_info *ctrl_info); +void sis_soft_reset(struct pqi_ctrl_info *ctrl_info); +int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info); + +#endif /* _SMARTPQI_SIS_H */ From bd77befa5bcff8c51613de271913639edf85fbc2 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:44 +0200 Subject: [PATCH 008/173] zfcp: fix fc_host port_type with NPIV For an NPIV-enabled FCP device, zfcp can erroneously show "NPort (fabric via point-to-point)" instead of "NPIV VPORT" for the port_type sysfs attribute of the corresponding fc_host. s390-tools that can be affected are dbginfo.sh and ziomon. zfcp_fsf_exchange_config_evaluate() ignores fsf_qtcb_bottom_config.connection_features indicating NPIV and only sets fc_host_port_type to FC_PORTTYPE_NPORT if fsf_qtcb_bottom_config.fc_topology is FSF_TOPO_FABRIC. Only the independent zfcp_fsf_exchange_port_evaluate() evaluates connection_features to overwrite fc_host_port_type to FC_PORTTYPE_NPIV in case of NPIV. Code was introduced with upstream kernel 2.6.30 commit 0282985da5923fa6365adcc1a1586ae0c13c1617 ("[SCSI] zfcp: Report fc_host_port_type as NPIV"). This works during FCP device recovery (such as set online) because it performs FSF_QTCB_EXCHANGE_CONFIG_DATA followed by FSF_QTCB_EXCHANGE_PORT_DATA in sequence. However, the zfcp-specific scsi host sysfs attributes "requests", "megabytes", or "seconds_active" trigger only zfcp_fsf_exchange_config_evaluate() resetting fc_host port_type to FC_PORTTYPE_NPORT despite NPIV. The zfcp-specific scsi host sysfs attribute "utilization" triggers only zfcp_fsf_exchange_port_evaluate() correcting the fc_host port_type again in case of NPIV. Evaluate fsf_qtcb_bottom_config.connection_features in zfcp_fsf_exchange_config_evaluate() where it belongs to. Signed-off-by: Steffen Maier Fixes: 0282985da592 ("[SCSI] zfcp: Report fc_host_port_type as NPIV") Cc: #2.6.30+ Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fsf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 522a633c866a..84353f45cfe6 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -3,7 +3,7 @@ * * Implementation of FSF commands. * - * Copyright IBM Corp. 2002, 2013 + * Copyright IBM Corp. 2002, 2015 */ #define KMSG_COMPONENT "zfcp" @@ -508,7 +508,10 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) fc_host_port_type(shost) = FC_PORTTYPE_PTP; break; case FSF_TOPO_FABRIC: - fc_host_port_type(shost) = FC_PORTTYPE_NPORT; + if (bottom->connection_features & FSF_FEATURE_NPIV_MODE) + fc_host_port_type(shost) = FC_PORTTYPE_NPIV; + else + fc_host_port_type(shost) = FC_PORTTYPE_NPORT; break; case FSF_TOPO_AL: fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; @@ -613,7 +616,6 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req) if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) { fc_host_permanent_port_name(shost) = bottom->wwpn; - fc_host_port_type(shost) = FC_PORTTYPE_NPIV; } else fc_host_permanent_port_name(shost) = fc_host_port_name(shost); fc_host_maxframe_size(shost) = bottom->maximum_frame_size; From 70369f8e15b220f50a16348c79a61d3f7054813c Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:45 +0200 Subject: [PATCH 009/173] zfcp: fix ELS/GS request&response length for hardware data router In the hardware data router case, introduced with kernel 3.2 commit 86a9668a8d29 ("[SCSI] zfcp: support for hardware data router") the ELS/GS request&response length needs to be initialized as in the chained SBAL case. Otherwise, the FCP channel rejects ELS requests with FSF_REQUEST_SIZE_TOO_LARGE. Such ELS requests can be issued by user space through BSG / HBA API, or zfcp itself uses ADISC ELS for remote port link test on RSCN. The latter can cause a short path outage due to unnecessary remote target port recovery because the always failing ADISC cannot detect extremely short path interruptions beyond the local FCP channel. Below example is decoded with zfcpdbf from s390-tools: Timestamp : ... Area : SAN Subarea : 00 Level : 1 Exception : - CPU id : .. Caller : zfcp_dbf_san_req+0408 Record id : 1 Tag : fssels1 Request id : 0x Destination ID : 0x00 Payload info : 52000000 00000000 [ADISC] 00 00000000 00000000 00000000 00000000 00000000 Timestamp : ... Area : HBA Subarea : 00 Level : 1 Exception : - CPU id : .. Caller : zfcp_dbf_hba_fsf_res+0740 Record id : 1 Tag : fs_ferr Request id : 0x Request status : 0x00000010 FSF cmnd : 0x0000000b [FSF_QTCB_SEND_ELS] FSF sequence no: 0x... FSF issued : ... FSF stat : 0x00000061 [FSF_REQUEST_SIZE_TOO_LARGE] FSF stat qual : 00000000 00000000 00000000 00000000 Prot stat : 0x00000100 Prot stat qual : 00000000 00000000 00000000 00000000 Signed-off-by: Steffen Maier Fixes: 86a9668a8d29 ("[SCSI] zfcp: support for hardware data router") Cc: # 3.2+ Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fsf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 84353f45cfe6..96d35a7209fa 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -984,8 +984,12 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, if (zfcp_adapter_multi_buffer_active(adapter)) { if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_req)) return -EIO; + qtcb->bottom.support.req_buf_length = + zfcp_qdio_real_bytes(sg_req); if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_resp)) return -EIO; + qtcb->bottom.support.resp_buf_length = + zfcp_qdio_real_bytes(sg_resp); zfcp_qdio_set_data_div(qdio, &req->qdio_req, zfcp_qdio_sbale_count(sg_req)); From 4eeaa4f3f1d6c47b69f70e222297a4df4743363e Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:46 +0200 Subject: [PATCH 010/173] zfcp: close window with unblocked rport during rport gone On a successful end of reopen port forced, zfcp_erp_strategy_followup_success() re-uses the port erp_action and the subsequent zfcp_erp_action_cleanup() now sees ZFCP_ERP_SUCCEEDED with erp_action->action==ZFCP_ERP_ACTION_REOPEN_PORT instead of ZFCP_ERP_ACTION_REOPEN_PORT_FORCED but must not perform zfcp_scsi_schedule_rport_register(). We can detect this because the fresh port reopen erp_action is in its very first step ZFCP_ERP_STEP_UNINITIALIZED. Otherwise this opens a time window with unblocked rport (until the followup port reopen recovery would block it again). If a scsi_cmnd timeout occurs during this time window fc_timed_out() cannot work as desired and such command would indeed time out and trigger scsi_eh. This prevents a clean and timely path failover. This should not happen if the path issue can be recovered on FC transport layer such as path issues involving RSCNs. Also, unnecessary and repeated DID_IMM_RETRY for pending and undesired new requests occur because internally zfcp still has its zfcp_port blocked. As follow-on errors with scsi_eh, it can cause, in the worst case, permanently lost paths due to one of: sd : [] Medium access timeout failure. Offlining disk! sd : Device offlined - not ready after error recovery For fix validation and to aid future debugging with other recoveries we now also trace (un)blocking of rports. Signed-off-by: Steffen Maier Fixes: 5767620c383a ("[SCSI] zfcp: Do not unblock rport from REOPEN_PORT_FORCED") Fixes: a2fa0aede07c ("[SCSI] zfcp: Block FC transport rports early on errors") Fixes: 5f852be9e11d ("[SCSI] zfcp: Fix deadlock between zfcp ERP and SCSI") Fixes: 338151e06608 ("[SCSI] zfcp: make use of fc_remote_port_delete when target port is unavailable") Fixes: 3859f6a248cb ("[PATCH] zfcp: add rports to enable scsi_add_device to work again") Cc: #2.6.32+ Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.h | 7 ++++++- drivers/s390/scsi/zfcp_erp.c | 12 +++++++++--- drivers/s390/scsi/zfcp_scsi.c | 8 +++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 0be3d48681ae..7901deb4ba89 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -2,7 +2,7 @@ * zfcp device driver * debug feature declarations * - * Copyright IBM Corp. 2008, 2010 + * Copyright IBM Corp. 2008, 2015 */ #ifndef ZFCP_DBF_H @@ -17,6 +17,11 @@ #define ZFCP_DBF_INVALID_LUN 0xFFFFFFFFFFFFFFFFull +enum zfcp_dbf_pseudo_erp_act_type { + ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD = 0xff, + ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL = 0xfe, +}; + /** * struct zfcp_dbf_rec_trigger - trace record for triggered recovery action * @ready: number of ready recovery actions diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 3fb410977014..a59d678125bd 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -3,7 +3,7 @@ * * Error Recovery Procedures (ERP). * - * Copyright IBM Corp. 2002, 2010 + * Copyright IBM Corp. 2002, 2015 */ #define KMSG_COMPONENT "zfcp" @@ -1217,8 +1217,14 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) break; case ZFCP_ERP_ACTION_REOPEN_PORT: - if (result == ZFCP_ERP_SUCCEEDED) - zfcp_scsi_schedule_rport_register(port); + /* This switch case might also happen after a forced reopen + * was successfully done and thus overwritten with a new + * non-forced reopen at `ersfs_2'. In this case, we must not + * do the clean-up of the non-forced version. + */ + if (act->step != ZFCP_ERP_STEP_UNINITIALIZED) + if (result == ZFCP_ERP_SUCCEEDED) + zfcp_scsi_schedule_rport_register(port); /* fall through */ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: put_device(&port->dev); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index b3c6ff49103b..9069f98a1817 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -3,7 +3,7 @@ * * Interface to Linux SCSI midlayer. * - * Copyright IBM Corp. 2002, 2013 + * Copyright IBM Corp. 2002, 2015 */ #define KMSG_COMPONENT "zfcp" @@ -556,6 +556,9 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port) ids.port_id = port->d_id; ids.roles = FC_RPORT_ROLE_FCP_TARGET; + zfcp_dbf_rec_trig("scpaddy", port->adapter, port, NULL, + ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD, + ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD); rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids); if (!rport) { dev_err(&port->adapter->ccw_device->dev, @@ -577,6 +580,9 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port) struct fc_rport *rport = port->rport; if (rport) { + zfcp_dbf_rec_trig("scpdely", port->adapter, port, NULL, + ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL, + ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL); fc_remote_port_delete(rport); port->rport = NULL; } From 35f040df97fa0e94c7851c054ec71533c88b4b81 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:47 +0200 Subject: [PATCH 011/173] zfcp: retain trace level for SCSI and HBA FSF response records While retaining the actual filtering according to trace level, the following commits started to write such filtered records with a hardcoded record level of 1 instead of the actual record level: commit 250a1352b95e1db3216e5c5d4f4365bea5122f4a ("[SCSI] zfcp: Redesign of the debug tracing for SCSI records.") commit a54ca0f62f953898b05549391ac2a8a4dad6482b ("[SCSI] zfcp: Redesign of the debug tracing for HBA records.") Now we can distinguish written records again for offline level filtering. Signed-off-by: Steffen Maier Fixes: 250a1352b95e ("[SCSI] zfcp: Redesign of the debug tracing for SCSI records.") Fixes: a54ca0f62f95 ("[SCSI] zfcp: Redesign of the debug tracing for HBA records.") Cc: #2.6.38+ Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 11 ++++++----- drivers/s390/scsi/zfcp_dbf.h | 4 ++-- drivers/s390/scsi/zfcp_ext.h | 7 ++++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 5d7fbe4e907e..2308253ddddc 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -3,7 +3,7 @@ * * Debug traces for zfcp. * - * Copyright IBM Corp. 2002, 2013 + * Copyright IBM Corp. 2002, 2015 */ #define KMSG_COMPONENT "zfcp" @@ -65,7 +65,7 @@ void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area, * @tag: tag indicating which kind of unsolicited status has been received * @req: request for which a response was received */ -void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req) +void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req) { struct zfcp_dbf *dbf = req->adapter->dbf; struct fsf_qtcb_prefix *q_pref = &req->qtcb->prefix; @@ -97,7 +97,7 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req) rec->pl_len, "fsf_res", req->req_id); } - debug_event(dbf->hba, 1, rec, sizeof(*rec)); + debug_event(dbf->hba, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->hba_lock, flags); } @@ -399,7 +399,8 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf) * @sc: pointer to struct scsi_cmnd * @fsf: pointer to struct zfcp_fsf_req */ -void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) +void zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *sc, + struct zfcp_fsf_req *fsf) { struct zfcp_adapter *adapter = (struct zfcp_adapter *) sc->device->host->hostdata[0]; @@ -442,7 +443,7 @@ void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) } } - debug_event(dbf->scsi, 1, rec, sizeof(*rec)); + debug_event(dbf->scsi, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->scsi_lock, flags); } diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 7901deb4ba89..c879b54046ee 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -284,7 +284,7 @@ static inline void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req) { if (debug_level_enabled(req->adapter->dbf->hba, level)) - zfcp_dbf_hba_fsf_res(tag, req); + zfcp_dbf_hba_fsf_res(tag, level, req); } /** @@ -323,7 +323,7 @@ void _zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *scmd, scmd->device->host->hostdata[0]; if (debug_level_enabled(adapter->dbf->scsi, level)) - zfcp_dbf_scsi(tag, scmd, req); + zfcp_dbf_scsi(tag, level, scmd, req); } /** diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 5b500652572b..fd03a943cde2 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -3,7 +3,7 @@ * * External function declarations. * - * Copyright IBM Corp. 2002, 2010 + * Copyright IBM Corp. 2002, 2015 */ #ifndef ZFCP_EXT_H @@ -36,7 +36,7 @@ extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *, struct zfcp_port *, struct scsi_device *, u8, u8); extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *); extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *); -extern void zfcp_dbf_hba_fsf_res(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **); @@ -44,7 +44,8 @@ extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *); extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32); extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *); -extern void zfcp_dbf_scsi(char *, struct scsi_cmnd *, struct zfcp_fsf_req *); +extern void zfcp_dbf_scsi(char *, int, struct scsi_cmnd *, + struct zfcp_fsf_req *); /* zfcp_erp.c */ extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32); From 0102a30a6ff60f4bb4c07358ca3b1f92254a6c25 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:48 +0200 Subject: [PATCH 012/173] zfcp: restore: Dont use 0 to indicate invalid LUN in rec trace bring back commit d21e9daa63e009ce5b87bbcaa6d11ce48e07bbbe ("[SCSI] zfcp: Dont use 0 to indicate invalid LUN in rec trace") which was lost with commit ae0904f60fab7cb20c48d32eefdd735e478b91fb ("[SCSI] zfcp: Redesign of the debug tracing for recovery actions.") Signed-off-by: Steffen Maier Fixes: ae0904f60fab ("[SCSI] zfcp: Redesign of the debug tracing for recovery actions.") Cc: #2.6.38+ Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 2308253ddddc..e6ff199f7572 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -241,7 +241,8 @@ static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec, if (sdev) { rec->lun_status = atomic_read(&sdev_to_zfcp(sdev)->status); rec->lun = zfcp_scsi_dev_lun(sdev); - } + } else + rec->lun = ZFCP_DBF_INVALID_LUN; } /** From d27a7cb91960cf1fdd11b10071e601828cbf4b1f Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:49 +0200 Subject: [PATCH 013/173] zfcp: trace on request for open and close of WKA port Since commit a54ca0f62f953898b05549391ac2a8a4dad6482b ("[SCSI] zfcp: Redesign of the debug tracing for HBA records.") HBA records no longer contain WWPN, D_ID, or LUN to reduce duplicate information which is already in REC records. In contrast to "regular" target ports, we don't use recovery to open WKA ports such as directory/nameserver, so we don't get REC records. Therefore, introduce pseudo REC running records without any actual recovery action but including D_ID of WKA port on open/close. Signed-off-by: Steffen Maier Fixes: a54ca0f62f95 ("[SCSI] zfcp: Redesign of the debug tracing for HBA records.") Cc: #2.6.38+ Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 32 ++++++++++++++++++++++++++++++++ drivers/s390/scsi/zfcp_ext.h | 1 + drivers/s390/scsi/zfcp_fsf.c | 8 ++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index e6ff199f7572..df68a4df8cdb 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -321,6 +321,38 @@ void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) spin_unlock_irqrestore(&dbf->rec_lock, flags); } +/** + * zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery + * @tag: identifier for event + * @wka_port: well known address port + * @req_id: request ID to correlate with potential HBA trace record + */ +void zfcp_dbf_rec_run_wka(char *tag, struct zfcp_fc_wka_port *wka_port, + u64 req_id) +{ + struct zfcp_dbf *dbf = wka_port->adapter->dbf; + struct zfcp_dbf_rec *rec = &dbf->rec_buf; + unsigned long flags; + + spin_lock_irqsave(&dbf->rec_lock, flags); + memset(rec, 0, sizeof(*rec)); + + rec->id = ZFCP_DBF_REC_RUN; + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->port_status = wka_port->status; + rec->d_id = wka_port->d_id; + rec->lun = ZFCP_DBF_INVALID_LUN; + + rec->u.run.fsf_req_id = req_id; + rec->u.run.rec_status = ~0; + rec->u.run.rec_step = ~0; + rec->u.run.rec_action = ~0; + rec->u.run.rec_count = ~0; + + debug_event(dbf->rec, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->rec_lock, flags); +} + static inline void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len, u64 req_id, u32 d_id) diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index fd03a943cde2..c8fed9fa1cca 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -35,6 +35,7 @@ extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *); extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *, struct zfcp_port *, struct scsi_device *, u8, u8); extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *); +extern void zfcp_dbf_rec_run_wka(char *, struct zfcp_fc_wka_port *, u64); extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 96d35a7209fa..f56906f09bb3 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1581,7 +1581,7 @@ out: int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) { struct zfcp_qdio *qdio = wka_port->adapter->qdio; - struct zfcp_fsf_req *req; + struct zfcp_fsf_req *req = NULL; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1610,6 +1610,8 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) zfcp_fsf_req_free(req); out: spin_unlock_irq(&qdio->req_q_lock); + if (req && !IS_ERR(req)) + zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id); return retval; } @@ -1634,7 +1636,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) { struct zfcp_qdio *qdio = wka_port->adapter->qdio; - struct zfcp_fsf_req *req; + struct zfcp_fsf_req *req = NULL; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1663,6 +1665,8 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) zfcp_fsf_req_free(req); out: spin_unlock_irq(&qdio->req_q_lock); + if (req && !IS_ERR(req)) + zfcp_dbf_rec_run_wka("fscwp_1", wka_port, req->req_id); return retval; } From 7c964ffe586bc0c3d9febe9bf97a2e4b2866e5b7 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:50 +0200 Subject: [PATCH 014/173] zfcp: restore tracing of handle for port and LUN with HBA records This information was lost with commit a54ca0f62f953898b05549391ac2a8a4dad6482b ("[SCSI] zfcp: Redesign of the debug tracing for HBA records.") but is required to debug e.g. invalid handle situations. Signed-off-by: Steffen Maier Fixes: a54ca0f62f95 ("[SCSI] zfcp: Redesign of the debug tracing for HBA records.") Cc: #2.6.38+ Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 2 ++ drivers/s390/scsi/zfcp_dbf.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index df68a4df8cdb..ceb934c93232 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -85,6 +85,8 @@ void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req) rec->u.res.req_issued = req->issued; rec->u.res.prot_status = q_pref->prot_status; rec->u.res.fsf_status = q_head->fsf_status; + rec->u.res.port_handle = q_head->port_handle; + rec->u.res.lun_handle = q_head->lun_handle; memcpy(rec->u.res.prot_status_qual, &q_pref->prot_status_qual, FSF_PROT_STATUS_QUAL_SIZE); diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index c879b54046ee..6ee46f1f9e43 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -131,6 +131,8 @@ struct zfcp_dbf_hba_res { u8 prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE]; u32 fsf_status; u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; + u32 port_handle; + u32 lun_handle; } __packed; /** From 771bf03537ddfa4a4dde62ef9dfbc82e4f77ab20 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:51 +0200 Subject: [PATCH 015/173] zfcp: fix D_ID field with actual value on tracing SAN responses With commit 2c55b750a884b86dea8b4cc5f15e1484cc47a25c ("[SCSI] zfcp: Redesign of the debug tracing for SAN records.") we lost the N_Port-ID where an ELS response comes from. With commit 7c7dc196814b9e1d5cc254dc579a5fa78ae524f7 ("[SCSI] zfcp: Simplify handling of ct and els requests") we lost the N_Port-ID where a CT response comes from. It's especially useful if the request SAN trace record with D_ID was already lost due to trace buffer wrap. GS uses an open WKA port handle and ELS just a D_ID, and only for ELS we could get D_ID from QTCB bottom via zfcp_fsf_req. To cover both cases, add a new field to zfcp_fsf_ct_els and fill it in on request to use in SAN response trace. Strictly speaking the D_ID on SAN response is the FC frame's S_ID. We don't need a field for the other end which is always us. Signed-off-by: Steffen Maier Fixes: 2c55b750a884 ("[SCSI] zfcp: Redesign of the debug tracing for SAN records.") Fixes: 7c7dc196814b ("[SCSI] zfcp: Simplify handling of ct and els requests") Cc: #2.6.38+ Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 2 +- drivers/s390/scsi/zfcp_fsf.c | 2 ++ drivers/s390/scsi/zfcp_fsf.h | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index ceb934c93232..968897f5e1c6 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -407,7 +407,7 @@ void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf) length = (u16)(ct_els->resp->length + FC_CT_HDR_LEN); zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length, - fsf->req_id, 0); + fsf->req_id, ct_els->d_id); } /** diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index f56906f09bb3..75f820ca17b7 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1079,6 +1079,7 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port, req->handler = zfcp_fsf_send_ct_handler; req->qtcb->header.port_handle = wka_port->handle; + ct->d_id = wka_port->d_id; req->data = ct; zfcp_dbf_san_req("fssct_1", req, wka_port->d_id); @@ -1175,6 +1176,7 @@ int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id, hton24(req->qtcb->bottom.support.d_id, d_id); req->handler = zfcp_fsf_send_els_handler; + els->d_id = d_id; req->data = els; zfcp_dbf_san_req("fssels1", req, d_id); diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 57ae3ae1046d..be1c04b334c5 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -3,7 +3,7 @@ * * Interface to the FSF support functions. * - * Copyright IBM Corp. 2002, 2010 + * Copyright IBM Corp. 2002, 2015 */ #ifndef FSF_H @@ -436,6 +436,7 @@ struct zfcp_blk_drv_data { * @handler_data: data passed to handler function * @port: Optional pointer to port for zfcp internal ELS (only test link ADISC) * @status: used to pass error status to calling function + * @d_id: Destination ID of either open WKA port for CT or of D_ID for ELS */ struct zfcp_fsf_ct_els { struct scatterlist *req; @@ -444,6 +445,7 @@ struct zfcp_fsf_ct_els { void *handler_data; struct zfcp_port *port; int status; + u32 d_id; }; #endif /* FSF_H */ From 94db3725f049ead24c96226df4a4fb375b880a77 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:52 +0200 Subject: [PATCH 016/173] zfcp: fix payload trace length for SAN request&response commit 2c55b750a884b86dea8b4cc5f15e1484cc47a25c ("[SCSI] zfcp: Redesign of the debug tracing for SAN records.") started to add FC_CT_HDR_LEN which made zfcp dump random data out of bounds for RSPN GS responses because u.rspn.rsp is the largest and last field in the union of struct zfcp_fc_req. Other request/response types only happened to stay within bounds due to the padding of the union or due to the trace capping of u.gspn.rsp to ZFCP_DBF_SAN_MAX_PAYLOAD. Timestamp : ... Area : SAN Subarea : 00 Level : 1 Exception : - CPU id : .. Caller : ... Record id : 2 Tag : fsscth2 Request id : 0x... Destination ID : 0x00fffffc Payload short : 01000000 fc020000 80020000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx <=== 00000000 00000000 00000000 00000000 Payload length : 32 <=== struct zfcp_fc_req { [0] struct zfcp_fsf_ct_els ct_els; [56] struct scatterlist sg_req; [96] struct scatterlist sg_rsp; union { struct {req; rsp;} adisc; SIZE: 28+28= 56 struct {req; rsp;} gid_pn; SIZE: 24+20= 44 struct {rspsg; req;} gpn_ft; SIZE: 40*4+20=180 struct {req; rsp;} gspn; SIZE: 20+273= 293 struct {req; rsp;} rspn; SIZE: 277+16= 293 [136] } u; } SIZE: 432 Signed-off-by: Steffen Maier Fixes: 2c55b750a884 ("[SCSI] zfcp: Redesign of the debug tracing for SAN records.") Cc: #2.6.38+ Reviewed-by: Alexey Ishchuk Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 968897f5e1c6..d8c0bfb40366 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -389,7 +389,7 @@ void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id) struct zfcp_fsf_ct_els *ct_els = fsf->data; u16 length; - length = (u16)(ct_els->req->length + FC_CT_HDR_LEN); + length = (u16)(ct_els->req->length); zfcp_dbf_san(tag, dbf, sg_virt(ct_els->req), ZFCP_DBF_SAN_REQ, length, fsf->req_id, d_id); } @@ -405,7 +405,7 @@ void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf) struct zfcp_fsf_ct_els *ct_els = fsf->data; u16 length; - length = (u16)(ct_els->resp->length + FC_CT_HDR_LEN); + length = (u16)(ct_els->resp->length); zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length, fsf->req_id, ct_els->d_id); } From aceeffbb59bb91404a0bda32a542d7ebf878433a Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 10 Aug 2016 18:30:53 +0200 Subject: [PATCH 017/173] zfcp: trace full payload of all SAN records (req,resp,iels) This was lost with commit 2c55b750a884b86dea8b4cc5f15e1484cc47a25c ("[SCSI] zfcp: Redesign of the debug tracing for SAN records.") but is necessary for problem determination, e.g. to see the currently active zone set during automatic port scan. For the large GPN_FT response (4 pages), save space by not dumping any empty residual entries. Signed-off-by: Steffen Maier Fixes: 2c55b750a884 ("[SCSI] zfcp: Redesign of the debug tracing for SAN records.") Cc: #2.6.38+ Reviewed-by: Alexey Ishchuk Reviewed-by: Benjamin Block Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 116 +++++++++++++++++++++++++++++++---- drivers/s390/scsi/zfcp_dbf.h | 1 + 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index d8c0bfb40366..637cf8973c9e 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -3,7 +3,7 @@ * * Debug traces for zfcp. * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #define KMSG_COMPONENT "zfcp" @@ -356,12 +356,15 @@ void zfcp_dbf_rec_run_wka(char *tag, struct zfcp_fc_wka_port *wka_port, } static inline -void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len, - u64 req_id, u32 d_id) +void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, + char *paytag, struct scatterlist *sg, u8 id, u16 len, + u64 req_id, u32 d_id, u16 cap_len) { struct zfcp_dbf_san *rec = &dbf->san_buf; u16 rec_len; unsigned long flags; + struct zfcp_dbf_pay *payload = &dbf->pay_buf; + u16 pay_sum = 0; spin_lock_irqsave(&dbf->san_lock, flags); memset(rec, 0, sizeof(*rec)); @@ -369,10 +372,41 @@ void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len, rec->id = id; rec->fsf_req_id = req_id; rec->d_id = d_id; - rec_len = min(len, (u16)ZFCP_DBF_SAN_MAX_PAYLOAD); - memcpy(rec->payload, data, rec_len); memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->pl_len = len; /* full length even if we cap pay below */ + if (!sg) + goto out; + rec_len = min_t(unsigned int, sg->length, ZFCP_DBF_SAN_MAX_PAYLOAD); + memcpy(rec->payload, sg_virt(sg), rec_len); /* part of 1st sg entry */ + if (len <= rec_len) + goto out; /* skip pay record if full content in rec->payload */ + /* if (len > rec_len): + * dump data up to cap_len ignoring small duplicate in rec->payload + */ + spin_lock_irqsave(&dbf->pay_lock, flags); + memset(payload, 0, sizeof(*payload)); + memcpy(payload->area, paytag, ZFCP_DBF_TAG_LEN); + payload->fsf_req_id = req_id; + payload->counter = 0; + for (; sg && pay_sum < cap_len; sg = sg_next(sg)) { + u16 pay_len, offset = 0; + + while (offset < sg->length && pay_sum < cap_len) { + pay_len = min((u16)ZFCP_DBF_PAY_MAX_REC, + (u16)(sg->length - offset)); + /* cap_len <= pay_sum < cap_len+ZFCP_DBF_PAY_MAX_REC */ + memcpy(payload->data, sg_virt(sg) + offset, pay_len); + debug_event(dbf->pay, 1, payload, + zfcp_dbf_plen(pay_len)); + payload->counter++; + offset += pay_len; + pay_sum += pay_len; + } + } + spin_unlock(&dbf->pay_lock); + +out: debug_event(dbf->san, 1, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->san_lock, flags); } @@ -389,9 +423,62 @@ void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id) struct zfcp_fsf_ct_els *ct_els = fsf->data; u16 length; - length = (u16)(ct_els->req->length); - zfcp_dbf_san(tag, dbf, sg_virt(ct_els->req), ZFCP_DBF_SAN_REQ, length, - fsf->req_id, d_id); + length = (u16)zfcp_qdio_real_bytes(ct_els->req); + zfcp_dbf_san(tag, dbf, "san_req", ct_els->req, ZFCP_DBF_SAN_REQ, + length, fsf->req_id, d_id, length); +} + +static u16 zfcp_dbf_san_res_cap_len_if_gpn_ft(char *tag, + struct zfcp_fsf_req *fsf, + u16 len) +{ + struct zfcp_fsf_ct_els *ct_els = fsf->data; + struct fc_ct_hdr *reqh = sg_virt(ct_els->req); + struct fc_ns_gid_ft *reqn = (struct fc_ns_gid_ft *)(reqh + 1); + struct scatterlist *resp_entry = ct_els->resp; + struct fc_gpn_ft_resp *acc; + int max_entries, x, last = 0; + + if (!(memcmp(tag, "fsscth2", 7) == 0 + && ct_els->d_id == FC_FID_DIR_SERV + && reqh->ct_rev == FC_CT_REV + && reqh->ct_in_id[0] == 0 + && reqh->ct_in_id[1] == 0 + && reqh->ct_in_id[2] == 0 + && reqh->ct_fs_type == FC_FST_DIR + && reqh->ct_fs_subtype == FC_NS_SUBTYPE + && reqh->ct_options == 0 + && reqh->_ct_resvd1 == 0 + && reqh->ct_cmd == FC_NS_GPN_FT + /* reqh->ct_mr_size can vary so do not match but read below */ + && reqh->_ct_resvd2 == 0 + && reqh->ct_reason == 0 + && reqh->ct_explan == 0 + && reqh->ct_vendor == 0 + && reqn->fn_resvd == 0 + && reqn->fn_domain_id_scope == 0 + && reqn->fn_area_id_scope == 0 + && reqn->fn_fc4_type == FC_TYPE_FCP)) + return len; /* not GPN_FT response so do not cap */ + + acc = sg_virt(resp_entry); + max_entries = (reqh->ct_mr_size * 4 / sizeof(struct fc_gpn_ft_resp)) + + 1 /* zfcp_fc_scan_ports: bytes correct, entries off-by-one + * to account for header as 1st pseudo "entry" */; + + /* the basic CT_IU preamble is the same size as one entry in the GPN_FT + * response, allowing us to skip special handling for it - just skip it + */ + for (x = 1; x < max_entries && !last; x++) { + if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1)) + acc++; + else + acc = sg_virt(++resp_entry); + + last = acc->fp_flags & FC_NS_FID_LAST; + } + len = min(len, (u16)(x * sizeof(struct fc_gpn_ft_resp))); + return len; /* cap after last entry */ } /** @@ -405,9 +492,10 @@ void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf) struct zfcp_fsf_ct_els *ct_els = fsf->data; u16 length; - length = (u16)(ct_els->resp->length); - zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length, - fsf->req_id, ct_els->d_id); + length = (u16)zfcp_qdio_real_bytes(ct_els->resp); + zfcp_dbf_san(tag, dbf, "san_res", ct_els->resp, ZFCP_DBF_SAN_RES, + length, fsf->req_id, ct_els->d_id, + zfcp_dbf_san_res_cap_len_if_gpn_ft(tag, fsf, length)); } /** @@ -421,11 +509,13 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf) struct fsf_status_read_buffer *srb = (struct fsf_status_read_buffer *) fsf->data; u16 length; + struct scatterlist sg; length = (u16)(srb->length - offsetof(struct fsf_status_read_buffer, payload)); - zfcp_dbf_san(tag, dbf, srb->payload.data, ZFCP_DBF_SAN_ELS, length, - fsf->req_id, ntoh24(srb->d_id)); + sg_init_one(&sg, srb->payload.data, length); + zfcp_dbf_san(tag, dbf, "san_els", &sg, ZFCP_DBF_SAN_ELS, length, + fsf->req_id, ntoh24(srb->d_id), length); } /** diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 6ee46f1f9e43..36d07584271d 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -115,6 +115,7 @@ struct zfcp_dbf_san { u32 d_id; #define ZFCP_DBF_SAN_MAX_PAYLOAD (FC_CT_HDR_LEN + 32) char payload[ZFCP_DBF_SAN_MAX_PAYLOAD]; + u16 pl_len; } __packed; /** From 166f310b629c046b7f5ca846adf978cda47b06c2 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 5 Aug 2016 14:55:00 +0200 Subject: [PATCH 018/173] scsi: libfc: Issue PRLI after a PRLO has been received When receiving a PRLO it just means that the operating parameters have changed, it does _not_ mean that the port doesn't want to communicate with us. So instead of implicitly logging out we should be issueing a PRLI to figure out the new operating parameters. We can always recover once PRLI fails. Signed-off-by: Hannes Reinecke Reviewed-by: Chad Dupuis Tested-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/libfc/fc_rport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 93f596182145..bd9983b3acf3 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -1877,7 +1877,7 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, spp->spp_type_ext = rspp->spp_type_ext; spp->spp_flags = FC_SPP_RESP_ACK; - fc_rport_enter_delete(rdata, RPORT_EV_LOGO); + fc_rport_enter_prli(rdata); fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); lport->tt.frame_send(lport, fp); From d391966a03846176a78ef8d53898de8b4302a2be Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 5 Aug 2016 14:55:01 +0200 Subject: [PATCH 019/173] scsi: libfc: send LOGO for PLOGI failure When running in point-to-multipoint mode PLOGI is done after FLOGI completed. So when the PLOGI fails we should be sending a LOGO to the remote port. [mkp: Applied by hand] Signed-off-by: Hannes Reinecke Reviewed-by: Chad Dupuis Tested-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/libfc/fc_rport.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index bd9983b3acf3..6bfed0f643d5 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -547,16 +547,24 @@ static void fc_rport_timeout(struct work_struct *work) */ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp) { + struct fc_lport *lport = rdata->local_port; + FC_RPORT_DBG(rdata, "Error %ld in state %s, retries %d\n", IS_ERR(fp) ? -PTR_ERR(fp) : 0, fc_rport_state(rdata), rdata->retries); switch (rdata->rp_state) { case RPORT_ST_FLOGI: - case RPORT_ST_PLOGI: rdata->flags &= ~FC_RP_STARTED; fc_rport_enter_delete(rdata, RPORT_EV_FAILED); break; + case RPORT_ST_PLOGI: + if (lport->point_to_multipoint) { + rdata->flags &= ~FC_RP_STARTED; + fc_rport_enter_delete(rdata, RPORT_EV_FAILED); + } else + fc_rport_enter_logo(rdata); + break; case RPORT_ST_RTV: fc_rport_enter_ready(rdata); break; From 649eb8693857e9b9fca009fba4eb7e80f9f3a326 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 5 Aug 2016 14:55:02 +0200 Subject: [PATCH 020/173] scsi: libfc: reset exchange manager during LOGO handling FC-LS mandates that we should invalidate all sequences before sending a LOGO. And we should set the event to RPORT_EV_STOP when a LOGO request has been received to signal that all exchanges are terminated. Signed-off-by: Hannes Reinecke Reviewed-by: Chad Dupuis Tested-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/libfc/fc_rport.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 6bfed0f643d5..97aeaddd600d 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -457,6 +457,9 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata, */ static int fc_rport_logoff(struct fc_rport_priv *rdata) { + struct fc_lport *lport = rdata->local_port; + u32 port_id = rdata->ids.port_id; + mutex_lock(&rdata->rp_mutex); FC_RPORT_DBG(rdata, "Remove port\n"); @@ -466,6 +469,15 @@ static int fc_rport_logoff(struct fc_rport_priv *rdata) FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n"); goto out; } + /* + * FC-LS states: + * To explicitly Logout, the initiating Nx_Port shall terminate + * other open Sequences that it initiated with the destination + * Nx_Port prior to performing Logout. + */ + lport->tt.exch_mgr_reset(lport, 0, port_id); + lport->tt.exch_mgr_reset(lport, port_id, 0); + fc_rport_enter_logo(rdata); /* @@ -1923,7 +1935,7 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n", fc_rport_state(rdata)); - fc_rport_enter_delete(rdata, RPORT_EV_LOGO); + fc_rport_enter_delete(rdata, RPORT_EV_STOP); mutex_unlock(&rdata->rp_mutex); kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy); } else From a850ced42916a9bf25f4595c744156e6b8b8f5c0 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 5 Aug 2016 14:55:03 +0200 Subject: [PATCH 021/173] scsi: libfc: do not send ABTS when resetting exchanges When all exchanges are reset the upper layers have already logged out of the remote port, so the exchanges can be reset without sending any ABTS. Signed-off-by: Hannes Reinecke Reviewed-by: Chad Dupuis Tested-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/libfc/fc_exch.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index e72673b0a8fb..16ca31ad5ec0 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -1837,7 +1837,6 @@ static void fc_exch_reset(struct fc_exch *ep) int rc = 1; spin_lock_bh(&ep->ex_lock); - fc_exch_abort_locked(ep, 0); ep->state |= FC_EX_RST_CLEANUP; fc_exch_timer_cancel(ep); if (ep->esb_stat & ESB_ST_REC_QUAL) From 41b99e1a30a6d04df7585905e5ffc7251099c6d3 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Tue, 9 Aug 2016 18:39:18 -0500 Subject: [PATCH 022/173] scsi: cxlflash: Avoid mutex when destroying context Context information structures are protected by a mutex that is held when accessing/manipulating the context. When the code that manages these structures was authored, a decision was made to include taking the mutex as part of the allocation/initialization sequence and also handle the scenario where the mutex was already held when freeing the context. While not a problem outright, this design decision has been deemed as too flexible and the code should be made more rigid to avoid future bugs. In addition, further review of the code yields that the existing mutex manipulations in both of these context management paths are superfluous. This commit removes the obtaining of the context mutex in the context initialization routine and assumes the mutex is not held in the context free path. Inspired-by: Al Viro Signed-off-by: Matthew R. Ochs Acked-by: Manoj N. Kumar Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/superpipe.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index ce1507023132..ab5c8939928b 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -709,14 +709,13 @@ int cxlflash_disk_release(struct scsi_device *sdev, * @cfg: Internal structure associated with the host. * @ctxi: Context to release. * - * This routine is safe to be called with a a non-initialized context - * and is tolerant of being called with the context's mutex held (it - * will be unlocked if necessary before freeing). Also note that the - * routine conditionally checks for the existence of the context control - * map before clearing the RHT registers and context capabilities because - * it is possible to destroy a context while the context is in the error - * state (previous mapping was removed [so there is no need to worry about - * clearing] and context is waiting for a new mapping). + * This routine is safe to be called with a a non-initialized context. + * Also note that the routine conditionally checks for the existence + * of the context control map before clearing the RHT registers and + * context capabilities because it is possible to destroy a context + * while the context is in the error state (previous mapping was + * removed [so there is no need to worry about clearing] and context + * is waiting for a new mapping). */ static void destroy_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) @@ -732,9 +731,6 @@ static void destroy_context(struct cxlflash_cfg *cfg, writeq_be(0, &ctxi->ctrl_map->rht_cnt_id); writeq_be(0, &ctxi->ctrl_map->ctx_cap); } - - if (mutex_is_locked(&ctxi->mutex)) - mutex_unlock(&ctxi->mutex); } /* Free memory associated with context */ @@ -795,9 +791,6 @@ err: * @adap_fd: Previously obtained adapter fd associated with CXL context. * @file: Previously obtained file associated with CXL context. * @perms: User-specified permissions. - * - * Upon return, the context is marked as initialized and the context's mutex - * is locked. */ static void init_context(struct ctx_info *ctxi, struct cxlflash_cfg *cfg, struct cxl_context *ctx, int ctxid, int adap_fd, @@ -816,8 +809,6 @@ static void init_context(struct ctx_info *ctxi, struct cxlflash_cfg *cfg, mutex_init(&ctxi->mutex); INIT_LIST_HEAD(&ctxi->luns); INIT_LIST_HEAD(&ctxi->list); /* initialize for list_empty() */ - - mutex_lock(&ctxi->mutex); } /** @@ -1445,7 +1436,6 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, * knows about us yet; we can be the only one holding our mutex. */ list_add(&lun_access->list, &ctxi->luns); - mutex_unlock(&ctxi->mutex); mutex_lock(&cfg->ctx_tbl_list_mutex); mutex_lock(&ctxi->mutex); cfg->ctx_tbl[ctxid] = ctxi; @@ -1494,7 +1484,7 @@ err: file = NULL; } - /* Cleanup our context; safe to call even with mutex locked */ + /* Cleanup our context */ if (ctxi) { destroy_context(cfg, ctxi); ctxi = NULL; From 44ef38f9a2af8644c24ace6cbe1132dc70174c39 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Tue, 9 Aug 2016 18:39:30 -0500 Subject: [PATCH 023/173] scsi: cxlflash: Cache owning adapter within context The context removal routine requires access to the owning adapter structure to reset the context within the AFU as part of the tear down sequence. In order to support kref adoption, the owning adapter must be accessible from the release handler. As the kref framework only provides the kref reference as the sole parameter, another means is needed to derive the owning adapter. As a remedy, the owning adapter reference is saved off within the context during initialization. Signed-off-by: Matthew R. Ochs Acked-by: Manoj N. Kumar Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/superpipe.c | 1 + drivers/scsi/cxlflash/superpipe.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index ab5c8939928b..640c3a2641ef 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -804,6 +804,7 @@ static void init_context(struct ctx_info *ctxi, struct cxlflash_cfg *cfg, ctxi->lfd = adap_fd; ctxi->pid = current->tgid; /* tgid = pid */ ctxi->ctx = ctx; + ctxi->cfg = cfg; ctxi->file = file; ctxi->initialized = true; mutex_init(&ctxi->mutex); diff --git a/drivers/scsi/cxlflash/superpipe.h b/drivers/scsi/cxlflash/superpipe.h index 5f9a091fda95..61404f201deb 100644 --- a/drivers/scsi/cxlflash/superpipe.h +++ b/drivers/scsi/cxlflash/superpipe.h @@ -107,6 +107,7 @@ struct ctx_info { bool err_recovery_active; struct mutex mutex; /* Context protection */ struct cxl_context *ctx; + struct cxlflash_cfg *cfg; struct list_head luns; /* LUNs attached to this context */ const struct vm_operations_struct *cxl_mmap_vmops; struct file *file; From 888baf069f49529f33c0b1dfb0fc4811dc0ca1d2 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Tue, 9 Aug 2016 18:39:42 -0500 Subject: [PATCH 024/173] scsi: cxlflash: Add kref to context Currently, context user references are tracked via the list of LUNs that have attached to the context. While convenient, this is not intuitive without a deep study of the code and is inconsistent with the existing reference tracking patterns within the kernel. This design choice can lead to future bug injection. To improve code comprehension and better protect against future bugs, add explicit reference counting to contexts and migrate the context removal code to the kref release handler. Inspired-by: Al Viro Signed-off-by: Matthew R. Ochs Acked-by: Manoj N. Kumar Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/superpipe.c | 87 ++++++++++++++++++------------- drivers/scsi/cxlflash/superpipe.h | 1 + 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index 640c3a2641ef..be7522ae02a4 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -808,10 +808,55 @@ static void init_context(struct ctx_info *ctxi, struct cxlflash_cfg *cfg, ctxi->file = file; ctxi->initialized = true; mutex_init(&ctxi->mutex); + kref_init(&ctxi->kref); INIT_LIST_HEAD(&ctxi->luns); INIT_LIST_HEAD(&ctxi->list); /* initialize for list_empty() */ } +/** + * remove_context() - context kref release handler + * @kref: Kernel reference associated with context to be removed. + * + * When a context no longer has any references it can safely be removed + * from global access and destroyed. Note that it is assumed the thread + * relinquishing access to the context holds its mutex. + */ +static void remove_context(struct kref *kref) +{ + struct ctx_info *ctxi = container_of(kref, struct ctx_info, kref); + struct cxlflash_cfg *cfg = ctxi->cfg; + int lfd; + u64 ctxid = DECODE_CTXID(ctxi->ctxid); + + /* Remove context from table/error list */ + WARN_ON(!mutex_is_locked(&ctxi->mutex)); + ctxi->unavail = true; + mutex_unlock(&ctxi->mutex); + mutex_lock(&cfg->ctx_tbl_list_mutex); + mutex_lock(&ctxi->mutex); + + if (!list_empty(&ctxi->list)) + list_del(&ctxi->list); + cfg->ctx_tbl[ctxid] = NULL; + mutex_unlock(&cfg->ctx_tbl_list_mutex); + mutex_unlock(&ctxi->mutex); + + /* Context now completely uncoupled/unreachable */ + lfd = ctxi->lfd; + destroy_context(cfg, ctxi); + + /* + * As a last step, clean up external resources when not + * already on an external cleanup thread, i.e.: close(adap_fd). + * + * NOTE: this will free up the context from the CXL services, + * allowing it to dole out the same context_id on a future + * (or even currently in-flight) disk_attach operation. + */ + if (lfd != -1) + sys_close(lfd); +} + /** * _cxlflash_disk_detach() - detaches a LUN from a context * @sdev: SCSI device associated with LUN. @@ -837,7 +882,6 @@ static int _cxlflash_disk_detach(struct scsi_device *sdev, int i; int rc = 0; - int lfd; u64 ctxid = DECODE_CTXID(detach->context_id), rctxid = detach->context_id; @@ -879,40 +923,12 @@ static int _cxlflash_disk_detach(struct scsi_device *sdev, break; } - /* Tear down context following last LUN cleanup */ - if (list_empty(&ctxi->luns)) { - ctxi->unavail = true; - mutex_unlock(&ctxi->mutex); - mutex_lock(&cfg->ctx_tbl_list_mutex); - mutex_lock(&ctxi->mutex); - - /* Might not have been in error list so conditionally remove */ - if (!list_empty(&ctxi->list)) - list_del(&ctxi->list); - cfg->ctx_tbl[ctxid] = NULL; - mutex_unlock(&cfg->ctx_tbl_list_mutex); - mutex_unlock(&ctxi->mutex); - - lfd = ctxi->lfd; - destroy_context(cfg, ctxi); - ctxi = NULL; - put_ctx = false; - - /* - * As a last step, clean up external resources when not - * already on an external cleanup thread, i.e.: close(adap_fd). - * - * NOTE: this will free up the context from the CXL services, - * allowing it to dole out the same context_id on a future - * (or even currently in-flight) disk_attach operation. - */ - if (lfd != -1) - sys_close(lfd); - } - - /* Release the sdev reference that bound this LUN to the context */ + /* + * Release the context reference and the sdev reference that + * bound this LUN to the context. + */ + put_ctx = !kref_put(&ctxi->kref, remove_context); scsi_device_put(sdev); - out: if (put_ctx) put_context(ctxi); @@ -1369,10 +1385,11 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, lun_access->lli = lli; lun_access->sdev = sdev; - /* Non-NULL context indicates reuse */ + /* Non-NULL context indicates reuse (another context reference) */ if (ctxi) { dev_dbg(dev, "%s: Reusing context for LUN! (%016llX)\n", __func__, rctxid); + kref_get(&ctxi->kref); list_add(&lun_access->list, &ctxi->luns); fd = ctxi->lfd; goto out_attach; diff --git a/drivers/scsi/cxlflash/superpipe.h b/drivers/scsi/cxlflash/superpipe.h index 61404f201deb..5bda8b5758d5 100644 --- a/drivers/scsi/cxlflash/superpipe.h +++ b/drivers/scsi/cxlflash/superpipe.h @@ -106,6 +106,7 @@ struct ctx_info { bool unavail; bool err_recovery_active; struct mutex mutex; /* Context protection */ + struct kref kref; struct cxl_context *ctx; struct cxlflash_cfg *cfg; struct list_head luns; /* LUNs attached to this context */ From cd34af40a09c678abad36304eb68e1774640e908 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Tue, 9 Aug 2016 18:39:52 -0500 Subject: [PATCH 025/173] scsi: cxlflash: Transition to application close model Caching the adapter file descriptor and performing a close on behalf of an application is a poor design. This is due to the fact that once a file descriptor in installed, it is free to be altered without the knowledge of the cxlflash driver. This can lead to inconsistencies between the application and kernel. Furthermore, the nature of the former design is more exploitable and thus should be abandoned. To support applications performing a close on the adapter file that is associated with a context, a new flag is introduced to the user API to indicate to applications that they are responsible for the close following the cleanup (detach) of a context. The documentation is also updated to reflect this change in behavior. Inspired-by: Al Viro Signed-off-by: Matthew R. Ochs Acked-by: Manoj N. Kumar Signed-off-by: Martin K. Petersen --- Documentation/powerpc/cxlflash.txt | 44 +++++++++++++++--- drivers/scsi/cxlflash/superpipe.c | 71 ++++++++---------------------- drivers/scsi/cxlflash/vlun.c | 13 +----- include/uapi/scsi/cxlflash_ioctl.h | 19 +++++--- 4 files changed, 74 insertions(+), 73 deletions(-) diff --git a/Documentation/powerpc/cxlflash.txt b/Documentation/powerpc/cxlflash.txt index 4202d1bc583c..f4c119092b98 100644 --- a/Documentation/powerpc/cxlflash.txt +++ b/Documentation/powerpc/cxlflash.txt @@ -171,11 +171,30 @@ DK_CXLFLASH_ATTACH destroyed, the tokens are to be considered stale and subsequent usage will result in errors. - - When a context is no longer needed, the user shall detach from - the context via the DK_CXLFLASH_DETACH ioctl. + - A valid adapter file descriptor (fd2 >= 0) is only returned on + the initial attach for a context. Subsequent attaches to an + existing context (DK_CXLFLASH_ATTACH_REUSE_CONTEXT flag present) + do not provide the adapter file descriptor as it was previously + made known to the application. - - A close on fd2 will invalidate the tokens. This operation is not - required by the user. + - When a context is no longer needed, the user shall detach from + the context via the DK_CXLFLASH_DETACH ioctl. When this ioctl + returns with a valid adapter file descriptor and the return flag + DK_CXLFLASH_APP_CLOSE_ADAP_FD is present, the application _must_ + close the adapter file descriptor following a successful detach. + + - When this ioctl returns with a valid fd2 and the return flag + DK_CXLFLASH_APP_CLOSE_ADAP_FD is present, the application _must_ + close fd2 in the following circumstances: + + + Following a successful detach of the last user of the context + + Following a successful recovery on the context's original fd2 + + In the child process of a fork(), following a clone ioctl, + on the fd2 associated with the source context + + - At any time, a close on fd2 will invalidate the tokens. Applications + should exercise caution to only close fd2 when appropriate (outlined + in the previous bullet) to avoid premature loss of I/O. DK_CXLFLASH_USER_DIRECT ----------------------- @@ -254,6 +273,10 @@ DK_CXLFLASH_DETACH success, all "tokens" which had been provided to the user from the DK_CXLFLASH_ATTACH onward are no longer valid. + When the DK_CXLFLASH_APP_CLOSE_ADAP_FD flag was returned on a successful + attach, the application _must_ close the fd2 associated with the context + following the detach of the final user of the context. + DK_CXLFLASH_VLUN_CLONE ---------------------- This ioctl is responsible for cloning a previously created @@ -261,7 +284,7 @@ DK_CXLFLASH_VLUN_CLONE support maintaining user space access to storage after a process forks. Upon success, the child process (which invoked the ioctl) will have access to the same LUNs via the same resource handle(s) - and fd2 as the parent, but under a different context. + as the parent, but under a different context. Context sharing across processes is not supported with CXL and therefore each fork must be met with establishing a new context @@ -275,6 +298,12 @@ DK_CXLFLASH_VLUN_CLONE translation tables are copied from the parent context to the child's and then synced with the AFU. + When the DK_CXLFLASH_APP_CLOSE_ADAP_FD flag was returned on a successful + attach, the application _must_ close the fd2 associated with the source + context (still resident/accessible in the parent process) following the + clone. This is to avoid a stale entry in the file descriptor table of the + child process. + DK_CXLFLASH_VERIFY ------------------ This ioctl is used to detect various changes such as the capacity of @@ -309,6 +338,11 @@ DK_CXLFLASH_RECOVER_AFU at which time the context/resources they held will be freed as part of the release fop. + When the DK_CXLFLASH_APP_CLOSE_ADAP_FD flag was returned on a successful + attach, the application _must_ unmap and close the fd2 associated with the + original context following this ioctl returning success and indicating that + the context was recovered (DK_CXLFLASH_RECOVER_AFU_CONTEXT_RESET). + DK_CXLFLASH_MANAGE_LUN ---------------------- This ioctl is used to switch a LUN from a mode where it is available diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index be7522ae02a4..b3bb90df4e1f 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -825,7 +825,6 @@ static void remove_context(struct kref *kref) { struct ctx_info *ctxi = container_of(kref, struct ctx_info, kref); struct cxlflash_cfg *cfg = ctxi->cfg; - int lfd; u64 ctxid = DECODE_CTXID(ctxi->ctxid); /* Remove context from table/error list */ @@ -842,19 +841,7 @@ static void remove_context(struct kref *kref) mutex_unlock(&ctxi->mutex); /* Context now completely uncoupled/unreachable */ - lfd = ctxi->lfd; destroy_context(cfg, ctxi); - - /* - * As a last step, clean up external resources when not - * already on an external cleanup thread, i.e.: close(adap_fd). - * - * NOTE: this will free up the context from the CXL services, - * allowing it to dole out the same context_id on a future - * (or even currently in-flight) disk_attach operation. - */ - if (lfd != -1) - sys_close(lfd); } /** @@ -949,34 +936,18 @@ static int cxlflash_disk_detach(struct scsi_device *sdev, * * This routine is the release handler for the fops registered with * the CXL services on an initial attach for a context. It is called - * when a close is performed on the adapter file descriptor returned - * to the user. Programmatically, the user is not required to perform - * the close, as it is handled internally via the detach ioctl when - * a context is being removed. Note that nothing prevents the user - * from performing a close, but the user should be aware that doing - * so is considered catastrophic and subsequent usage of the superpipe - * API with previously saved off tokens will fail. + * when a close (explicity by the user or as part of a process tear + * down) is performed on the adapter file descriptor returned to the + * user. The user should be aware that explicitly performing a close + * considered catastrophic and subsequent usage of the superpipe API + * with previously saved off tokens will fail. * - * When initiated from an external close (either by the user or via - * a process tear down), the routine derives the context reference - * and calls detach for each LUN associated with the context. The - * final detach operation will cause the context itself to be freed. - * Note that the saved off lfd is reset prior to calling detach to - * signify that the final detach should not perform a close. - * - * When initiated from a detach operation as part of the tear down - * of a context, the context is first completely freed and then the - * close is performed. This routine will fail to derive the context - * reference (due to the context having already been freed) and then - * call into the CXL release entry point. - * - * Thus, with exception to when the CXL process element (context id) - * lookup fails (a case that should theoretically never occur), every - * call into this routine results in a complete freeing of a context. - * - * As part of the detach, all per-context resources associated with the LUN - * are cleaned up. When detaching the last LUN for a context, the context - * itself is cleaned up and released. + * This routine derives the context reference and calls detach for + * each LUN associated with the context.The final detach operation + * causes the context itself to be freed. With exception to when the + * CXL process element (context id) lookup fails (a case that should + * theoretically never occur), every call into this routine results + * in a complete freeing of a context. * * Return: 0 on success */ @@ -1014,11 +985,8 @@ static int cxlflash_cxl_release(struct inode *inode, struct file *file) goto out; } - dev_dbg(dev, "%s: close(%d) for context %d\n", - __func__, ctxi->lfd, ctxid); + dev_dbg(dev, "%s: close for context %d\n", __func__, ctxid); - /* Reset the file descriptor to indicate we're on a close() thread */ - ctxi->lfd = -1; detach.context_id = ctxi->ctxid; list_for_each_entry_safe(lun_access, t, &ctxi->luns, list) _cxlflash_disk_detach(lun_access->sdev, ctxi, &detach); @@ -1391,7 +1359,6 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, __func__, rctxid); kref_get(&ctxi->kref); list_add(&lun_access->list, &ctxi->luns); - fd = ctxi->lfd; goto out_attach; } @@ -1461,7 +1428,11 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, fd_install(fd, file); out_attach: - attach->hdr.return_flags = 0; + if (fd != -1) + attach->hdr.return_flags = DK_CXLFLASH_APP_CLOSE_ADAP_FD; + else + attach->hdr.return_flags = 0; + attach->context_id = ctxi->ctxid; attach->block_size = gli->blk_len; attach->mmio_size = sizeof(afu->afu_map->hosts[0].harea); @@ -1526,7 +1497,7 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) { struct device *dev = &cfg->dev->dev; int rc = 0; - int old_fd, fd = -1; + int fd = -1; int ctxid = -1; struct file *file; struct cxl_context *ctx; @@ -1574,7 +1545,6 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) * No error paths after this point. Once the fd is installed it's * visible to user space and can't be undone safely on this thread. */ - old_fd = ctxi->lfd; ctxi->ctxid = ENCODE_CTXID(ctxi, ctxid); ctxi->lfd = fd; ctxi->ctx = ctx; @@ -1593,9 +1563,6 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) cfg->ctx_tbl[ctxid] = ctxi; mutex_unlock(&cfg->ctx_tbl_list_mutex); fd_install(fd, file); - - /* Release the original adapter fd and associated CXL resources */ - sys_close(old_fd); out: dev_dbg(dev, "%s: returning ctxid=%d fd=%d rc=%d\n", __func__, ctxid, fd, rc); @@ -1707,7 +1674,7 @@ retry_recover: recover->context_id = ctxi->ctxid; recover->adap_fd = ctxi->lfd; recover->mmio_size = sizeof(afu->afu_map->hosts[0].harea); - recover->hdr.return_flags |= + recover->hdr.return_flags = DK_CXLFLASH_APP_CLOSE_ADAP_FD | DK_CXLFLASH_RECOVER_AFU_CONTEXT_RESET; goto out; } diff --git a/drivers/scsi/cxlflash/vlun.c b/drivers/scsi/cxlflash/vlun.c index 50f8e9300770..90c5d7f5278e 100644 --- a/drivers/scsi/cxlflash/vlun.c +++ b/drivers/scsi/cxlflash/vlun.c @@ -1135,14 +1135,13 @@ int cxlflash_disk_clone(struct scsi_device *sdev, ctxid_dst = DECODE_CTXID(clone->context_id_dst), rctxid_src = clone->context_id_src, rctxid_dst = clone->context_id_dst; - int adap_fd_src = clone->adap_fd_src; int i, j; int rc = 0; bool found; LIST_HEAD(sidecar); - pr_debug("%s: ctxid_src=%llu ctxid_dst=%llu adap_fd_src=%d\n", - __func__, ctxid_src, ctxid_dst, adap_fd_src); + pr_debug("%s: ctxid_src=%llu ctxid_dst=%llu\n", + __func__, ctxid_src, ctxid_dst); /* Do not clone yourself */ if (unlikely(rctxid_src == rctxid_dst)) { @@ -1166,13 +1165,6 @@ int cxlflash_disk_clone(struct scsi_device *sdev, goto out; } - if (unlikely(adap_fd_src != ctxi_src->lfd)) { - pr_debug("%s: Invalid source adapter fd! (%d)\n", - __func__, adap_fd_src); - rc = -EINVAL; - goto out; - } - /* Verify there is no open resource handle in the destination context */ for (i = 0; i < MAX_RHT_PER_CONTEXT; i++) if (ctxi_dst->rht_start[i].nmask != 0) { @@ -1257,7 +1249,6 @@ int cxlflash_disk_clone(struct scsi_device *sdev, out_success: list_splice(&sidecar, &ctxi_dst->luns); - sys_close(adap_fd_src); /* fall through */ out: diff --git a/include/uapi/scsi/cxlflash_ioctl.h b/include/uapi/scsi/cxlflash_ioctl.h index 2302f3ce5f86..6bf1f8a022b1 100644 --- a/include/uapi/scsi/cxlflash_ioctl.h +++ b/include/uapi/scsi/cxlflash_ioctl.h @@ -39,19 +39,28 @@ struct dk_cxlflash_hdr { * at this time, this provides future flexibility. */ #define DK_CXLFLASH_ALL_PORTS_ACTIVE 0x0000000000000001ULL +#define DK_CXLFLASH_APP_CLOSE_ADAP_FD 0x0000000000000002ULL /* - * Notes: - * ----- + * General Notes: + * ------------- * The 'context_id' field of all ioctl structures contains the context * identifier for a context in the lower 32-bits (upper 32-bits are not * to be used when identifying a context to the AFU). That said, the value * in its entirety (all 64-bits) is to be treated as an opaque cookie and * should be presented as such when issuing ioctls. + */ + +/* + * DK_CXLFLASH_ATTACH Notes: + * ------------------------ + * Read/write access permissions are specified via the O_RDONLY, O_WRONLY, + * and O_RDWR flags defined in the fcntl.h header file. * - * For DK_CXLFLASH_ATTACH ioctl, user specifies read/write access - * permissions via the O_RDONLY, O_WRONLY, and O_RDWR flags defined in - * the fcntl.h header file. + * A valid adapter file descriptor (fd >= 0) is only returned on the initial + * attach (successful) of a context. When a context is shared(reused), the user + * is expected to already 'know' the adapter file descriptor associated with the + * context. */ #define DK_CXLFLASH_ATTACH_REUSE_CONTEXT 0x8000000000000000ULL From de9f0b0cbb86da288a2d38e35f2953a85608a6aa Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Tue, 9 Aug 2016 18:40:02 -0500 Subject: [PATCH 026/173] scsi: cxlflash: Remove adapter file descriptor cache The adapter file descriptor was previously cached within the kernel for a given context in order to support performing a close on behalf of an application. This is no longer needed as applications are now required to perform a close on the adapter file descriptor. Inspired-by: Al Viro Signed-off-by: Matthew R. Ochs Acked-by: Manoj N. Kumar Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/superpipe.c | 26 +++++++++++++------------- drivers/scsi/cxlflash/superpipe.h | 1 - 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index b3bb90df4e1f..c91fe6fe8d08 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -788,20 +788,18 @@ err: * @cfg: Internal structure associated with the host. * @ctx: Previously obtained CXL context reference. * @ctxid: Previously obtained process element associated with CXL context. - * @adap_fd: Previously obtained adapter fd associated with CXL context. * @file: Previously obtained file associated with CXL context. * @perms: User-specified permissions. */ static void init_context(struct ctx_info *ctxi, struct cxlflash_cfg *cfg, - struct cxl_context *ctx, int ctxid, int adap_fd, - struct file *file, u32 perms) + struct cxl_context *ctx, int ctxid, struct file *file, + u32 perms) { struct afu *afu = cfg->afu; ctxi->rht_perms = perms; ctxi->ctrl_map = &afu->afu_map->ctrls[ctxid].ctrl; ctxi->ctxid = ENCODE_CTXID(ctxi, ctxid); - ctxi->lfd = adap_fd; ctxi->pid = current->tgid; /* tgid = pid */ ctxi->ctx = ctx; ctxi->cfg = cfg; @@ -1086,8 +1084,7 @@ static int cxlflash_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) goto err; } - dev_dbg(dev, "%s: fault(%d) for context %d\n", - __func__, ctxi->lfd, ctxid); + dev_dbg(dev, "%s: fault for context %d\n", __func__, ctxid); if (likely(!ctxi->err_recovery_active)) { vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); @@ -1162,8 +1159,7 @@ static int cxlflash_cxl_mmap(struct file *file, struct vm_area_struct *vma) goto out; } - dev_dbg(dev, "%s: mmap(%d) for context %d\n", - __func__, ctxi->lfd, ctxid); + dev_dbg(dev, "%s: mmap for context %d\n", __func__, ctxid); rc = cxl_fd_mmap(file, vma); if (likely(!rc)) { @@ -1406,7 +1402,7 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, perms = SISL_RHT_PERM(attach->hdr.flags + 1); /* Context mutex is locked upon return */ - init_context(ctxi, cfg, ctx, ctxid, fd, file, perms); + init_context(ctxi, cfg, ctx, ctxid, file, perms); rc = afu_attach(cfg, ctxi); if (unlikely(rc)) { @@ -1488,12 +1484,15 @@ err: * recover_context() - recovers a context in error * @cfg: Internal structure associated with the host. * @ctxi: Context to release. + * @adap_fd: Adapter file descriptor associated with new/recovered context. * * Restablishes the state for a context-in-error. * * Return: 0 on success, -errno on failure */ -static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) +static int recover_context(struct cxlflash_cfg *cfg, + struct ctx_info *ctxi, + int *adap_fd) { struct device *dev = &cfg->dev->dev; int rc = 0; @@ -1546,7 +1545,6 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) * visible to user space and can't be undone safely on this thread. */ ctxi->ctxid = ENCODE_CTXID(ctxi, ctxid); - ctxi->lfd = fd; ctxi->ctx = ctx; ctxi->file = file; @@ -1563,6 +1561,7 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) cfg->ctx_tbl[ctxid] = ctxi; mutex_unlock(&cfg->ctx_tbl_list_mutex); fd_install(fd, file); + *adap_fd = fd; out: dev_dbg(dev, "%s: returning ctxid=%d fd=%d rc=%d\n", __func__, ctxid, fd, rc); @@ -1621,6 +1620,7 @@ static int cxlflash_afu_recover(struct scsi_device *sdev, rctxid = recover->context_id; long reg; int lretry = 20; /* up to 2 seconds */ + int new_adap_fd = -1; int rc = 0; atomic_inc(&cfg->recovery_threads); @@ -1650,7 +1650,7 @@ retry: if (ctxi->err_recovery_active) { retry_recover: - rc = recover_context(cfg, ctxi); + rc = recover_context(cfg, ctxi, &new_adap_fd); if (unlikely(rc)) { dev_err(dev, "%s: Recovery failed for context %llu (rc=%d)\n", __func__, ctxid, rc); @@ -1672,7 +1672,7 @@ retry_recover: ctxi->err_recovery_active = false; recover->context_id = ctxi->ctxid; - recover->adap_fd = ctxi->lfd; + recover->adap_fd = new_adap_fd; recover->mmio_size = sizeof(afu->afu_map->hosts[0].harea); recover->hdr.return_flags = DK_CXLFLASH_APP_CLOSE_ADAP_FD | DK_CXLFLASH_RECOVER_AFU_CONTEXT_RESET; diff --git a/drivers/scsi/cxlflash/superpipe.h b/drivers/scsi/cxlflash/superpipe.h index 5bda8b5758d5..9e62ff304e4b 100644 --- a/drivers/scsi/cxlflash/superpipe.h +++ b/drivers/scsi/cxlflash/superpipe.h @@ -100,7 +100,6 @@ struct ctx_info { struct cxl_ioctl_start_work work; u64 ctxid; - int lfd; pid_t pid; bool initialized; bool unavail; From 9442c9b0ed5c6f3a86dce0d6f714ef43f7f6cd53 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Tue, 9 Aug 2016 18:40:13 -0500 Subject: [PATCH 027/173] scsi: cxlflash: Update documentation Update the block library link in the API documentation. Signed-off-by: Matthew R. Ochs Acked-by: Manoj N. Kumar Signed-off-by: Martin K. Petersen --- Documentation/powerpc/cxlflash.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/powerpc/cxlflash.txt b/Documentation/powerpc/cxlflash.txt index f4c119092b98..6d9a2ed32cad 100644 --- a/Documentation/powerpc/cxlflash.txt +++ b/Documentation/powerpc/cxlflash.txt @@ -121,7 +121,7 @@ Block library API below. The block library can be found on GitHub: - http://www.github.com/mikehollinger/ibmcapikv + http://github.com/open-power/capiflash CXL Flash Driver IOCTLs From 0b924e5505a568e7a70e07b529a02ed12295ff7c Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 19 Aug 2016 15:33:37 +0200 Subject: [PATCH 028/173] scsi: fcoe: provide translation table between Ethernet and FC port speeds Provide a translation table between Ethernet and FC port speeds so odd speeds (from a Ethernet POV) like 8 Gbit are correctly mapped to sysfs and open-fcoe's fcoeadm. Before: Description: BCM57840 NetXtreme II 10/20-Gigabit Ethernet Revision: 11 Manufacturer: Broadcom Corporation Serial Number: 6CC2173EA1D0 Driver: bnx2x 1.712.30-0 Number of Ports: 1 Symbolic Name: bnx2fc (QLogic BCM57840) v2.10.3 over eth2 OS Device Name: host1 Node Name: 0x20006cc2173ea1d1 Port Name: 0x10006cc2173ea1d1 FabricName: 0x100000c0dd0ce717 Speed: unknown Supported Speed: 1 Gbit, 10 Gbit MaxFrameSize: 2048 bytes FC-ID (Port ID): 0x660702 State: Online After: Description: BCM57840 NetXtreme II 10/20-Gigabit Ethernet Revision: 11 Manufacturer: Broadcom Corporation Serial Number: 6CC2173EA1D0 Driver: bnx2x 1.712.30-0 Number of Ports: 1 Symbolic Name: bnx2fc (QLogic BCM57840) v2.10.3 over eth2 OS Device Name: host1 Node Name: 0x20006cc2173ea1d1 Port Name: 0x10006cc2173ea1d1 FabricName: 0x100000c0dd0ce717 Speed: 8 Gbit Supported Speed: 1 Gbit, 10 Gbit MaxFrameSize: 2048 bytes FC-ID (Port ID): 0x660701 State: Online Signed-off-by: Johannes Thumshirn Reviewed-by: Hannes Reinicke Reviewed-by: Lee Duncan Signed-off-by: Martin K. Petersen --- drivers/scsi/fcoe/fcoe_transport.c | 53 ++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 7028dd37e5dd..c164eec54308 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -83,6 +83,41 @@ static struct notifier_block libfcoe_notifier = { .notifier_call = libfcoe_device_notification, }; +static const struct { + u32 fc_port_speed; +#define SPEED_2000 2000 +#define SPEED_4000 4000 +#define SPEED_8000 8000 +#define SPEED_16000 16000 +#define SPEED_32000 32000 + u32 eth_port_speed; +} fcoe_port_speed_mapping[] = { + { FC_PORTSPEED_1GBIT, SPEED_1000 }, + { FC_PORTSPEED_2GBIT, SPEED_2000 }, + { FC_PORTSPEED_4GBIT, SPEED_4000 }, + { FC_PORTSPEED_8GBIT, SPEED_8000 }, + { FC_PORTSPEED_10GBIT, SPEED_10000 }, + { FC_PORTSPEED_16GBIT, SPEED_16000 }, + { FC_PORTSPEED_20GBIT, SPEED_20000 }, + { FC_PORTSPEED_25GBIT, SPEED_25000 }, + { FC_PORTSPEED_32GBIT, SPEED_32000 }, + { FC_PORTSPEED_40GBIT, SPEED_40000 }, + { FC_PORTSPEED_50GBIT, SPEED_50000 }, + { FC_PORTSPEED_100GBIT, SPEED_100000 }, +}; + +static inline u32 eth2fc_speed(u32 eth_port_speed) +{ + int i; + + for (i = 0; i <= ARRAY_SIZE(fcoe_port_speed_mapping); i++) { + if (fcoe_port_speed_mapping[i].eth_port_speed == eth_port_speed) + return fcoe_port_speed_mapping[i].fc_port_speed; + } + + return FC_PORTSPEED_UNKNOWN; +} + /** * fcoe_link_speed_update() - Update the supported and actual link speeds * @lport: The local port to update speeds for @@ -126,23 +161,7 @@ int fcoe_link_speed_update(struct fc_lport *lport) SUPPORTED_40000baseLR4_Full)) lport->link_supported_speeds |= FC_PORTSPEED_40GBIT; - switch (ecmd.base.speed) { - case SPEED_1000: - lport->link_speed = FC_PORTSPEED_1GBIT; - break; - case SPEED_10000: - lport->link_speed = FC_PORTSPEED_10GBIT; - break; - case SPEED_20000: - lport->link_speed = FC_PORTSPEED_20GBIT; - break; - case SPEED_40000: - lport->link_speed = FC_PORTSPEED_40GBIT; - break; - default: - lport->link_speed = FC_PORTSPEED_UNKNOWN; - break; - } + lport->link_speed = eth2fc_speed(ecmd.base.speed); return 0; } return -1; From ea89604f3d8dc49e9b28020bd25880aabd84c6cd Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:19:58 +0530 Subject: [PATCH 029/173] scsi: be2iscsi: Fix to use correct configuration values Following configuration is created with what driver exports: iface.vlan_id = 65535 iface.vlan_priority = 255 iface.vlan_state = vlan_state is empty as iscsiadm doesn't process "Disabled". When applying this configuration, iscsiadm checks for if vlan_state is "disable" if not it enables with value in vlan_id. 65535 not being valid value, 0 is applied. Use "enable" or "disable" for ISCSI_NET_PARAM. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_iscsi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 09f89a3eaa87..ab696ec29652 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -567,8 +567,8 @@ static int be2iscsi_get_if_param(struct beiscsi_hba *phba, break; case ISCSI_NET_PARAM_VLAN_ENABLED: len = sprintf(buf, "%s\n", - (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) - ? "Disabled\n" : "Enabled\n"); + (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) ? + "disable" : "enable"); break; case ISCSI_NET_PARAM_VLAN_ID: if (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) @@ -620,7 +620,7 @@ int be2iscsi_iface_get_param(struct iscsi_iface *iface, len = be2iscsi_get_if_param(phba, iface, param, buf); break; case ISCSI_NET_PARAM_IFACE_ENABLE: - len = sprintf(buf, "enabled\n"); + len = sprintf(buf, "enable\n"); break; case ISCSI_NET_PARAM_IPV4_GW: memset(&gateway, 0, sizeof(gateway)); From 96eb8d4df5c024028c6bea0a3eecdffe988ec71d Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:19:59 +0530 Subject: [PATCH 030/173] scsi: be2iscsi: Replace _bh version for mcc_lock spinlock This got unnecessarily introduced with other changes in previous commits. mcc_lock is taken only in process contexts. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index a55eaeea37e7..0ef3d5a43d25 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -133,7 +133,7 @@ struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, struct be_mcc_wrb *wrb = NULL; unsigned int tag; - spin_lock_bh(&phba->ctrl.mcc_lock); + spin_lock(&phba->ctrl.mcc_lock); if (mccq->used == mccq->len) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, @@ -174,7 +174,7 @@ struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, mccq->used++; alloc_failed: - spin_unlock_bh(&phba->ctrl.mcc_lock); + spin_unlock(&phba->ctrl.mcc_lock); return wrb; } @@ -182,7 +182,7 @@ void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag) { struct be_queue_info *mccq = &ctrl->mcc_obj.q; - spin_lock_bh(&ctrl->mcc_lock); + spin_lock(&ctrl->mcc_lock); tag = tag & MCC_Q_CMD_TAG_MASK; ctrl->mcc_tag[ctrl->mcc_free_index] = tag; if (ctrl->mcc_free_index == (MAX_MCC_CMD - 1)) @@ -191,7 +191,7 @@ void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag) ctrl->mcc_free_index++; ctrl->mcc_tag_available++; mccq->used--; - spin_unlock_bh(&ctrl->mcc_lock); + spin_unlock(&ctrl->mcc_lock); } /** From 3c9e36a9d819112f5c5d6c408a5786a8eadb78fa Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:00 +0530 Subject: [PATCH 031/173] scsi: be2iscsi: Reduce driver load/unload time Driver takes significant time to load 1m:20s and unload 40s. Checkpatch script threw warning: WARNING: msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt To eliminate this warning msleep(1) was replaced with msleep(20) before submitting. msleep(20) in init and uninit path for creation and destroying of number of WRBQs, CQs, and EQs is adding to load/unload time. Replace msleep with schedule_timeout_uninterruptible of 1ms as its enough in most cases. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 0ef3d5a43d25..59b8e2dc3aca 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -655,7 +655,8 @@ static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl) if (time_after(jiffies, timeout)) break; - msleep(20); + /* 1ms sleep is enough in most cases */ + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); } while (!ready); beiscsi_log(phba, KERN_ERR, From f7dd017e133e3b2d0395156f8d1ed16be732a6a7 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:01 +0530 Subject: [PATCH 032/173] scsi: be2iscsi: Set and return right iface v4/v6 states ipv4_iface and ipv6_iface fields need to be set to NULL when destroyed. Before creation these are checked. Use these to report correct states. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_iscsi.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index ab696ec29652..eab0f4fdbb31 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -292,10 +292,14 @@ void beiscsi_create_def_ifaces(struct beiscsi_hba *phba) void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba) { - if (phba->ipv6_iface) + if (phba->ipv6_iface) { iscsi_destroy_iface(phba->ipv6_iface); - if (phba->ipv4_iface) + phba->ipv6_iface = NULL; + } + if (phba->ipv4_iface) { iscsi_destroy_iface(phba->ipv4_iface); + phba->ipv4_iface = NULL; + } } static int @@ -406,6 +410,14 @@ beiscsi_set_ipv4(struct Scsi_Host *shost, /* Check the param */ switch (iface_param->param) { + case ISCSI_NET_PARAM_IFACE_ENABLE: + if (iface_param->value[0] == ISCSI_IFACE_ENABLE) + ret = beiscsi_create_ipv4_iface(phba); + else { + iscsi_destroy_iface(phba->ipv4_iface); + phba->ipv4_iface = NULL; + } + break; case ISCSI_NET_PARAM_IPV4_GW: ret = mgmt_set_gateway(phba, iface_param); break; @@ -421,12 +433,6 @@ beiscsi_set_ipv4(struct Scsi_Host *shost, "BS_%d : Invalid BOOTPROTO: %d\n", iface_param->value[0]); break; - case ISCSI_NET_PARAM_IFACE_ENABLE: - if (iface_param->value[0] == ISCSI_IFACE_ENABLE) - ret = beiscsi_create_ipv4_iface(phba); - else - iscsi_destroy_iface(phba->ipv4_iface); - break; case ISCSI_NET_PARAM_IPV4_SUBNET: case ISCSI_NET_PARAM_IPV4_ADDR: ret = beiscsi_set_static_ip(shost, iface_param, @@ -459,7 +465,7 @@ beiscsi_set_ipv6(struct Scsi_Host *shost, ret = beiscsi_create_ipv6_iface(phba); else { iscsi_destroy_iface(phba->ipv6_iface); - ret = 0; + phba->ipv6_iface = NULL; } break; case ISCSI_NET_PARAM_IPV6_ADDR: @@ -620,7 +626,12 @@ int be2iscsi_iface_get_param(struct iscsi_iface *iface, len = be2iscsi_get_if_param(phba, iface, param, buf); break; case ISCSI_NET_PARAM_IFACE_ENABLE: - len = sprintf(buf, "enable\n"); + if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) + len = sprintf(buf, "%s\n", + phba->ipv4_iface ? "enable" : "disable"); + else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) + len = sprintf(buf, "%s\n", + phba->ipv6_iface ? "enable" : "disable"); break; case ISCSI_NET_PARAM_IPV4_GW: memset(&gateway, 0, sizeof(gateway)); From 37f216482a72ea8ffc2935b2e7e926ba1241a2dd Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:02 +0530 Subject: [PATCH 033/173] scsi: be2iscsi: Fix gateway APIs to support IPv4 & IPv6 Gateway APIs assume IP type as IPv4. Modify it to be generic to allow clearing of IPv6 gateway set using BIOS. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_iscsi.c | 4 +- drivers/scsi/be2iscsi/be_mgmt.c | 126 ++++++++++++++----------------- drivers/scsi/be2iscsi/be_mgmt.h | 7 +- 3 files changed, 63 insertions(+), 74 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index eab0f4fdbb31..3ebfeff42653 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -419,7 +419,7 @@ beiscsi_set_ipv4(struct Scsi_Host *shost, } break; case ISCSI_NET_PARAM_IPV4_GW: - ret = mgmt_set_gateway(phba, iface_param); + ret = beiscsi_if_set_gw(phba, BE2_IPV4, iface_param->value); break; case ISCSI_NET_PARAM_IPV4_BOOTPROTO: if (iface_param->value[0] == ISCSI_BOOTPROTO_DHCP) @@ -635,7 +635,7 @@ int be2iscsi_iface_get_param(struct iscsi_iface *iface, break; case ISCSI_NET_PARAM_IPV4_GW: memset(&gateway, 0, sizeof(gateway)); - len = mgmt_get_gateway(phba, BE2_IPV4, &gateway); + len = beiscsi_if_get_gw(phba, BE2_IPV4, &gateway); if (!len) len = sprintf(buf, "%pI4\n", &gateway.ip_addr.addr); break; diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 83926e221f1e..9ef9f76d00d1 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -1059,14 +1059,13 @@ mgmt_static_ip_modify(struct beiscsi_hba *phba, return rc; } -static int mgmt_modify_gateway(struct beiscsi_hba *phba, uint8_t *gt_addr, - uint32_t gtway_action, uint32_t param_len) +static int beiscsi_if_mod_gw(struct beiscsi_hba *phba, + u32 action, u32 ip_type, u8 *gw) { struct be_cmd_set_def_gateway_req *req; struct be_dma_mem nonemb_cmd; int rt_val; - rt_val = mgmt_alloc_cmd_data(phba, &nonemb_cmd, OPCODE_COMMON_ISCSI_NTWK_MODIFY_DEFAULT_GATEWAY, sizeof(*req)); @@ -1074,14 +1073,62 @@ static int mgmt_modify_gateway(struct beiscsi_hba *phba, uint8_t *gt_addr, return rt_val; req = nonemb_cmd.va; - req->action = gtway_action; - req->ip_addr.ip_type = BE2_IPV4; - - memcpy(req->ip_addr.addr, gt_addr, sizeof(req->ip_addr.addr)); - + req->action = action; + req->ip_addr.ip_type = ip_type; + memcpy(req->ip_addr.addr, gw, + (ip_type == BE2_IPV4) ? IP_V4_LEN : IP_V6_LEN); return mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); } +int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw) +{ + struct be_cmd_get_def_gateway_resp gw_resp; + int rt_val; + + memset(&gw_resp, 0, sizeof(gw_resp)); + rt_val = beiscsi_if_get_gw(phba, ip_type, &gw_resp); + if (rt_val) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to Get Gateway Addr\n"); + return rt_val; + } + + rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, ip_type, + gw_resp.ip_addr.addr); + if (rt_val) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to clear Gateway Addr Set\n"); + return rt_val; + } + + rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_ADD, ip_type, gw); + if (rt_val) + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to Set Gateway Addr\n"); + + return rt_val; +} + +int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, + struct be_cmd_get_def_gateway_resp *resp) +{ + struct be_cmd_get_def_gateway_req *req; + struct be_dma_mem nonemb_cmd; + int rc; + + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + OPCODE_COMMON_ISCSI_NTWK_GET_DEFAULT_GATEWAY, + sizeof(*resp)); + if (rc) + return rc; + + req = nonemb_cmd.va; + req->ip_type = ip_type; + + return mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, resp, + sizeof(*resp)); +} + int mgmt_set_ip(struct beiscsi_hba *phba, struct iscsi_iface_param_info *ip_param, struct iscsi_iface_param_info *subnet_param, @@ -1156,7 +1203,7 @@ int mgmt_set_ip(struct beiscsi_hba *phba, /* Delete the Gateway settings if mode change is to DHCP */ if (boot_proto == ISCSI_BOOTPROTO_DHCP) { memset(>way_addr_set, 0, sizeof(gtway_addr_set)); - rc = mgmt_get_gateway(phba, BE2_IPV4, >way_addr_set); + rc = beiscsi_if_get_gw(phba, BE2_IPV4, >way_addr_set); if (rc) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, "BG_%d : Failed to Get Gateway Addr\n"); @@ -1165,8 +1212,8 @@ int mgmt_set_ip(struct beiscsi_hba *phba, if (gtway_addr_set.ip_addr.addr[0]) { gtway_addr = (uint8_t *)>way_addr_set.ip_addr.addr; - rc = mgmt_modify_gateway(phba, gtway_addr, - IP_ACTION_DEL, IP_V4_LEN); + rc = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, + ip_type, gtway_addr); if (rc) { beiscsi_log(phba, KERN_WARNING, @@ -1202,63 +1249,6 @@ exit: return rc; } -int mgmt_set_gateway(struct beiscsi_hba *phba, - struct iscsi_iface_param_info *gateway_param) -{ - struct be_cmd_get_def_gateway_resp gtway_addr_set; - uint8_t *gtway_addr; - int rt_val; - - memset(>way_addr_set, 0, sizeof(gtway_addr_set)); - rt_val = mgmt_get_gateway(phba, BE2_IPV4, >way_addr_set); - if (rt_val) { - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Get Gateway Addr\n"); - return rt_val; - } - - if (gtway_addr_set.ip_addr.addr[0]) { - gtway_addr = (uint8_t *)>way_addr_set.ip_addr.addr; - rt_val = mgmt_modify_gateway(phba, gtway_addr, IP_ACTION_DEL, - gateway_param->len); - if (rt_val) { - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to clear Gateway Addr Set\n"); - return rt_val; - } - } - - gtway_addr = (uint8_t *)&gateway_param->value; - rt_val = mgmt_modify_gateway(phba, gtway_addr, IP_ACTION_ADD, - gateway_param->len); - - if (rt_val) - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Set Gateway Addr\n"); - - return rt_val; -} - -int mgmt_get_gateway(struct beiscsi_hba *phba, int ip_type, - struct be_cmd_get_def_gateway_resp *gateway) -{ - struct be_cmd_get_def_gateway_req *req; - struct be_dma_mem nonemb_cmd; - int rc; - - rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, - OPCODE_COMMON_ISCSI_NTWK_GET_DEFAULT_GATEWAY, - sizeof(*gateway)); - if (rc) - return rc; - - req = nonemb_cmd.va; - req->ip_type = ip_type; - - return mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, gateway, - sizeof(*gateway)); -} - int mgmt_get_if_info(struct beiscsi_hba *phba, int ip_type, struct be_cmd_get_if_info_resp **if_info) { diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index f3a48a04b2ca..fd9b68f318c0 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -298,11 +298,10 @@ int mgmt_get_nic_conf(struct beiscsi_hba *phba, int mgmt_get_if_info(struct beiscsi_hba *phba, int ip_type, struct be_cmd_get_if_info_resp **if_info); -int mgmt_get_gateway(struct beiscsi_hba *phba, int ip_type, - struct be_cmd_get_def_gateway_resp *gateway); +int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, + struct be_cmd_get_def_gateway_resp *resp); -int mgmt_set_gateway(struct beiscsi_hba *phba, - struct iscsi_iface_param_info *gateway_param); +int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw); int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba, unsigned int *s_handle); From 0152a7e9710c1a7557dacee0318f9475336efd2d Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:03 +0530 Subject: [PATCH 034/173] scsi: be2iscsi: Fix release of DHCP IP in static mode If BOOTPROTO is changed to static, the DHCP IP address should be released. All cases are being handled mgmt_set_ip and mgmt_static_ip_modify. Rearrange IFACE APIs to: beiscsi_if_clr_ip beiscsi_if_set_ip beiscsi_if_en_static beiscsi_if_en_dhcp This simplifies release of DHCP IP when BOOTPROTO is set to static. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_iscsi.c | 125 +++++-------- drivers/scsi/be2iscsi/be_mgmt.c | 298 ++++++++++++++++--------------- drivers/scsi/be2iscsi/be_mgmt.h | 8 +- 3 files changed, 204 insertions(+), 227 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 3ebfeff42653..7e17a8ddcac8 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -302,58 +302,6 @@ void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba) } } -static int -beiscsi_set_static_ip(struct Scsi_Host *shost, - struct iscsi_iface_param_info *iface_param, - void *data, uint32_t dt_len) -{ - struct beiscsi_hba *phba = iscsi_host_priv(shost); - struct iscsi_iface_param_info *iface_ip = NULL; - struct iscsi_iface_param_info *iface_subnet = NULL; - struct nlattr *nla; - int ret; - - - switch (iface_param->param) { - case ISCSI_NET_PARAM_IPV4_BOOTPROTO: - nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR); - if (nla) - iface_ip = nla_data(nla); - - nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET); - if (nla) - iface_subnet = nla_data(nla); - break; - case ISCSI_NET_PARAM_IPV4_ADDR: - iface_ip = iface_param; - nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET); - if (nla) - iface_subnet = nla_data(nla); - break; - case ISCSI_NET_PARAM_IPV4_SUBNET: - iface_subnet = iface_param; - nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR); - if (nla) - iface_ip = nla_data(nla); - break; - default: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Unsupported param %d\n", - iface_param->param); - } - - if (!iface_ip || !iface_subnet) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : IP and Subnet Mask required\n"); - return -EINVAL; - } - - ret = mgmt_set_ip(phba, iface_ip, iface_subnet, - ISCSI_BOOTPROTO_STATIC); - - return ret; -} - /** * beiscsi_set_vlan_tag()- Set the VLAN TAG * @shost: Scsi Host for the driver instance @@ -401,17 +349,19 @@ beiscsi_set_vlan_tag(struct Scsi_Host *shost, static int -beiscsi_set_ipv4(struct Scsi_Host *shost, - struct iscsi_iface_param_info *iface_param, - void *data, uint32_t dt_len) +beiscsi_iface_config_ipv4(struct Scsi_Host *shost, + struct iscsi_iface_param_info *info, + void *data, uint32_t dt_len) { struct beiscsi_hba *phba = iscsi_host_priv(shost); + u8 *ip = NULL, *subnet = NULL, *gw; + struct nlattr *nla; int ret = 0; /* Check the param */ - switch (iface_param->param) { + switch (info->param) { case ISCSI_NET_PARAM_IFACE_ENABLE: - if (iface_param->value[0] == ISCSI_IFACE_ENABLE) + if (info->value[0] == ISCSI_IFACE_ENABLE) ret = beiscsi_create_ipv4_iface(phba); else { iscsi_destroy_iface(phba->ipv4_iface); @@ -419,42 +369,59 @@ beiscsi_set_ipv4(struct Scsi_Host *shost, } break; case ISCSI_NET_PARAM_IPV4_GW: - ret = beiscsi_if_set_gw(phba, BE2_IPV4, iface_param->value); + gw = info->value; + ret = beiscsi_if_set_gw(phba, BE2_IPV4, gw); break; case ISCSI_NET_PARAM_IPV4_BOOTPROTO: - if (iface_param->value[0] == ISCSI_BOOTPROTO_DHCP) - ret = mgmt_set_ip(phba, iface_param, - NULL, ISCSI_BOOTPROTO_DHCP); - else if (iface_param->value[0] == ISCSI_BOOTPROTO_STATIC) - ret = beiscsi_set_static_ip(shost, iface_param, - data, dt_len); + if (info->value[0] == ISCSI_BOOTPROTO_DHCP) + ret = beiscsi_if_en_dhcp(phba, BE2_IPV4); + else if (info->value[0] == ISCSI_BOOTPROTO_STATIC) + /* release DHCP IP address */ + ret = beiscsi_if_en_static(phba, BE2_IPV4, NULL, NULL); else beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : Invalid BOOTPROTO: %d\n", - iface_param->value[0]); + info->value[0]); + break; + case ISCSI_NET_PARAM_IPV4_ADDR: + ip = info->value; + nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET); + if (nla) { + info = nla_data(nla); + subnet = info->value; + } + ret = beiscsi_if_en_static(phba, BE2_IPV4, ip, subnet); break; case ISCSI_NET_PARAM_IPV4_SUBNET: - case ISCSI_NET_PARAM_IPV4_ADDR: - ret = beiscsi_set_static_ip(shost, iface_param, - data, dt_len); + /* + * OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR ioctl needs IP + * and subnet both. Find IP to be applied for this subnet. + */ + subnet = info->value; + nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR); + if (nla) { + info = nla_data(nla); + ip = info->value; + } + ret = beiscsi_if_en_static(phba, BE2_IPV4, ip, subnet); break; case ISCSI_NET_PARAM_VLAN_ENABLED: case ISCSI_NET_PARAM_VLAN_TAG: - ret = beiscsi_set_vlan_tag(shost, iface_param); + ret = beiscsi_set_vlan_tag(shost, info); break; default: beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : Param %d not supported\n", - iface_param->param); + info->param); } return ret; } static int -beiscsi_set_ipv6(struct Scsi_Host *shost, - struct iscsi_iface_param_info *iface_param, - void *data, uint32_t dt_len) +beiscsi_iface_config_ipv6(struct Scsi_Host *shost, + struct iscsi_iface_param_info *iface_param, + void *data, uint32_t dt_len) { struct beiscsi_hba *phba = iscsi_host_priv(shost); int ret = 0; @@ -469,8 +436,8 @@ beiscsi_set_ipv6(struct Scsi_Host *shost, } break; case ISCSI_NET_PARAM_IPV6_ADDR: - ret = mgmt_set_ip(phba, iface_param, NULL, - ISCSI_BOOTPROTO_STATIC); + ret = beiscsi_if_en_static(phba, BE2_IPV6, + iface_param->value, NULL); break; case ISCSI_NET_PARAM_VLAN_ENABLED: case ISCSI_NET_PARAM_VLAN_TAG: @@ -520,12 +487,12 @@ int be2iscsi_iface_set_param(struct Scsi_Host *shost, switch (iface_param->iface_type) { case ISCSI_IFACE_TYPE_IPV4: - ret = beiscsi_set_ipv4(shost, iface_param, - data, dt_len); + ret = beiscsi_iface_config_ipv4(shost, iface_param, + data, dt_len); break; case ISCSI_IFACE_TYPE_IPV6: - ret = beiscsi_set_ipv6(shost, iface_param, - data, dt_len); + ret = beiscsi_iface_config_ipv6(shost, iface_param, + data, dt_len); break; default: beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 9ef9f76d00d1..2a5d65e27c14 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -1004,61 +1004,6 @@ static int mgmt_alloc_cmd_data(struct beiscsi_hba *phba, struct be_dma_mem *cmd, return 0; } -static int -mgmt_static_ip_modify(struct beiscsi_hba *phba, - struct be_cmd_get_if_info_resp *if_info, - struct iscsi_iface_param_info *ip_param, - struct iscsi_iface_param_info *subnet_param, - uint32_t ip_action) -{ - struct be_cmd_set_ip_addr_req *req; - struct be_dma_mem nonemb_cmd; - uint32_t ip_type; - int rc; - - rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, - OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR, - sizeof(*req)); - if (rc) - return rc; - - ip_type = (ip_param->param == ISCSI_NET_PARAM_IPV6_ADDR) ? - BE2_IPV6 : BE2_IPV4 ; - - req = nonemb_cmd.va; - req->ip_params.record_entry_count = 1; - req->ip_params.ip_record.action = ip_action; - req->ip_params.ip_record.interface_hndl = - phba->interface_handle; - req->ip_params.ip_record.ip_addr.size_of_structure = - sizeof(struct be_ip_addr_subnet_format); - req->ip_params.ip_record.ip_addr.ip_type = ip_type; - - if (ip_action == IP_ACTION_ADD) { - memcpy(req->ip_params.ip_record.ip_addr.addr, ip_param->value, - sizeof(req->ip_params.ip_record.ip_addr.addr)); - - if (subnet_param) - memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, - subnet_param->value, - sizeof(req->ip_params.ip_record.ip_addr.subnet_mask)); - } else { - memcpy(req->ip_params.ip_record.ip_addr.addr, - if_info->ip_addr.addr, - sizeof(req->ip_params.ip_record.ip_addr.addr)); - - memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, - if_info->ip_addr.subnet_mask, - sizeof(req->ip_params.ip_record.ip_addr.subnet_mask)); - } - - rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); - if (rc < 0) - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Modify existing IP Address\n"); - return rc; -} - static int beiscsi_if_mod_gw(struct beiscsi_hba *phba, u32 action, u32 ip_type, u8 *gw) { @@ -1129,120 +1074,185 @@ int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, sizeof(*resp)); } -int mgmt_set_ip(struct beiscsi_hba *phba, - struct iscsi_iface_param_info *ip_param, - struct iscsi_iface_param_info *subnet_param, - uint32_t boot_proto) +static int +beiscsi_if_clr_ip(struct beiscsi_hba *phba, + struct be_cmd_get_if_info_resp *if_info) { - struct be_cmd_get_def_gateway_resp gtway_addr_set; - struct be_cmd_get_if_info_resp *if_info; - struct be_cmd_set_dhcp_req *dhcpreq; - struct be_cmd_rel_dhcp_req *reldhcp; + struct be_cmd_set_ip_addr_req *req; struct be_dma_mem nonemb_cmd; - uint8_t *gtway_addr; - uint32_t ip_type; int rc; - rc = mgmt_get_all_if_id(phba); + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR, + sizeof(*req)); if (rc) return rc; - ip_type = (ip_param->param == ISCSI_NET_PARAM_IPV6_ADDR) ? - BE2_IPV6 : BE2_IPV4 ; + req = nonemb_cmd.va; + req->ip_params.record_entry_count = 1; + req->ip_params.ip_record.action = IP_ACTION_DEL; + req->ip_params.ip_record.interface_hndl = + phba->interface_handle; + req->ip_params.ip_record.ip_addr.size_of_structure = + sizeof(struct be_ip_addr_subnet_format); + req->ip_params.ip_record.ip_addr.ip_type = if_info->ip_addr.ip_type; + memcpy(req->ip_params.ip_record.ip_addr.addr, + if_info->ip_addr.addr, + sizeof(if_info->ip_addr.addr)); + memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, + if_info->ip_addr.subnet_mask, + sizeof(if_info->ip_addr.subnet_mask)); + rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); + if (rc < 0 || req->ip_params.ip_record.status) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BG_%d : failed to clear IP: rc %d status %d\n", + rc, req->ip_params.ip_record.status); + } + return rc; +} + +static int +beiscsi_if_set_ip(struct beiscsi_hba *phba, u8 *ip, + u8 *subnet, u32 ip_type) +{ + struct be_cmd_set_ip_addr_req *req; + struct be_dma_mem nonemb_cmd; + uint32_t ip_len; + int rc; + + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR, + sizeof(*req)); + if (rc) + return rc; + + req = nonemb_cmd.va; + req->ip_params.record_entry_count = 1; + req->ip_params.ip_record.action = IP_ACTION_ADD; + req->ip_params.ip_record.interface_hndl = + phba->interface_handle; + req->ip_params.ip_record.ip_addr.size_of_structure = + sizeof(struct be_ip_addr_subnet_format); + req->ip_params.ip_record.ip_addr.ip_type = ip_type; + ip_len = ip_type == BE2_IPV4 ? IP_V4_LEN : IP_V6_LEN; + memcpy(req->ip_params.ip_record.ip_addr.addr, ip, ip_len); + if (subnet) + memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, + subnet, ip_len); + + rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); + /** + * In some cases, host needs to look into individual record status + * even though FW reported success for that IOCTL. + */ + if (rc < 0 || req->ip_params.ip_record.status) { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : failed to set IP: rc %d status %d\n", + rc, req->ip_params.ip_record.status); + if (req->ip_params.ip_record.status) + rc = -EINVAL; + } + return rc; +} + +int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, + u8 *ip, u8 *subnet) +{ + struct be_cmd_get_if_info_resp *if_info; + struct be_cmd_rel_dhcp_req *reldhcp; + struct be_dma_mem nonemb_cmd; + int rc; rc = mgmt_get_if_info(phba, ip_type, &if_info); if (rc) return rc; - if (boot_proto == ISCSI_BOOTPROTO_DHCP) { - if (if_info->dhcp_state) { - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : DHCP Already Enabled\n"); - goto exit; - } - /* The ip_param->len is 1 in DHCP case. Setting - proper IP len as this it is used while - freeing the Static IP. - */ - ip_param->len = (ip_param->param == ISCSI_NET_PARAM_IPV6_ADDR) ? - IP_V6_LEN : IP_V4_LEN; - - } else { - if (if_info->dhcp_state) { - - memset(if_info, 0, sizeof(*if_info)); - rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + if (if_info->dhcp_state) { + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, OPCODE_COMMON_ISCSI_NTWK_REL_STATELESS_IP_ADDR, sizeof(*reldhcp)); - - if (rc) - goto exit; - - reldhcp = nonemb_cmd.va; - reldhcp->interface_hndl = phba->interface_handle; - reldhcp->ip_type = ip_type; - - rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); - if (rc < 0) { - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Delete existing dhcp\n"); - goto exit; - } - } - } - - /* Delete the Static IP Set */ - if (if_info->ip_addr.addr[0]) { - rc = mgmt_static_ip_modify(phba, if_info, ip_param, NULL, - IP_ACTION_DEL); if (rc) goto exit; - } - /* Delete the Gateway settings if mode change is to DHCP */ - if (boot_proto == ISCSI_BOOTPROTO_DHCP) { - memset(>way_addr_set, 0, sizeof(gtway_addr_set)); - rc = beiscsi_if_get_gw(phba, BE2_IPV4, >way_addr_set); - if (rc) { + reldhcp = nonemb_cmd.va; + reldhcp->interface_hndl = phba->interface_handle; + reldhcp->ip_type = ip_type; + rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); + if (rc < 0) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Get Gateway Addr\n"); + "BG_%d : failed to release existing DHCP: %d\n", + rc); goto exit; } - - if (gtway_addr_set.ip_addr.addr[0]) { - gtway_addr = (uint8_t *)>way_addr_set.ip_addr.addr; - rc = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, - ip_type, gtway_addr); - - if (rc) { - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_CONFIG, - "BG_%d : Failed to clear Gateway Addr Set\n"); - goto exit; - } - } } - /* Set Adapter to DHCP/Static Mode */ - if (boot_proto == ISCSI_BOOTPROTO_DHCP) { - rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + /* first delete any old IP set */ + rc = beiscsi_if_clr_ip(phba, if_info); + if (rc) + goto exit; + + /* if ip == NULL then this is called just to release DHCP IP */ + if (ip) + rc = beiscsi_if_set_ip(phba, ip, subnet, ip_type); +exit: + kfree(if_info); + return rc; +} + +int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type) +{ + struct be_cmd_get_def_gateway_resp gw_resp; + struct be_cmd_get_if_info_resp *if_info; + struct be_cmd_set_dhcp_req *dhcpreq; + struct be_dma_mem nonemb_cmd; + u8 *gw; + int rc; + + rc = mgmt_get_if_info(phba, ip_type, &if_info); + if (rc) + return rc; + + if (if_info->dhcp_state) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : DHCP Already Enabled\n"); + goto exit; + } + + /* first delete any old static IP set */ + rc = beiscsi_if_clr_ip(phba, if_info); + if (rc) + goto exit; + + /* delete gateway settings if mode change is to DHCP */ + memset(&gw_resp, 0, sizeof(gw_resp)); + /* use ip_type provided in if_info */ + rc = beiscsi_if_get_gw(phba, if_info->ip_addr.ip_type, &gw_resp); + if (rc) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to Get Gateway Addr\n"); + goto exit; + } + gw = (u8 *)&gw_resp.ip_addr.addr; + rc = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, + if_info->ip_addr.ip_type, gw); + if (rc) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to clear Gateway Addr Set\n"); + goto exit; + } + + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, OPCODE_COMMON_ISCSI_NTWK_CONFIG_STATELESS_IP_ADDR, sizeof(*dhcpreq)); - if (rc) - goto exit; + if (rc) + goto exit; - dhcpreq = nonemb_cmd.va; - dhcpreq->flags = BLOCKING; - dhcpreq->retry_count = 1; - dhcpreq->interface_hndl = phba->interface_handle; - dhcpreq->ip_type = BE2_DHCP_V4; - - rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); - } else { - rc = mgmt_static_ip_modify(phba, if_info, ip_param, - subnet_param, IP_ACTION_ADD); - } + dhcpreq = nonemb_cmd.va; + dhcpreq->flags = BLOCKING; + dhcpreq->retry_count = 1; + dhcpreq->interface_hndl = phba->interface_handle; + dhcpreq->ip_type = ip_type; + rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); exit: kfree(if_info); diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index fd9b68f318c0..71a96097f83a 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -277,10 +277,10 @@ unsigned int mgmt_invalidate_connection(struct beiscsi_hba *phba, unsigned short issue_reset, unsigned short savecfg_flag); -int mgmt_set_ip(struct beiscsi_hba *phba, - struct iscsi_iface_param_info *ip_param, - struct iscsi_iface_param_info *subnet_param, - uint32_t boot_proto); +int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type); + +int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, + u8 *ip, u8 *subnet); unsigned int mgmt_get_boot_target(struct beiscsi_hba *phba); From db02aea993e8e43d4e9c30bd123027d323b03fd7 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:04 +0530 Subject: [PATCH 035/173] scsi: be2iscsi: Move VLAN code to common iface_set_param VLAN tag is L2 construct, move VLAN code out from configuring IP. Rearrange and rename the APIs to make it consistent. Replace ENOSYS with EPERM. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_iscsi.c | 89 ++++++++++++++++---------------- drivers/scsi/be2iscsi/be_mgmt.c | 75 ++++++++++++++------------- drivers/scsi/be2iscsi/be_mgmt.h | 2 +- 3 files changed, 84 insertions(+), 82 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 7e17a8ddcac8..b725536e6c9e 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -315,8 +315,8 @@ void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba) * Failure: Non-Zero Value **/ static int -beiscsi_set_vlan_tag(struct Scsi_Host *shost, - struct iscsi_iface_param_info *iface_param) +beiscsi_iface_config_vlan(struct Scsi_Host *shost, + struct iscsi_iface_param_info *iface_param) { struct beiscsi_hba *phba = iscsi_host_priv(shost); int ret; @@ -329,20 +329,17 @@ beiscsi_set_vlan_tag(struct Scsi_Host *shost, return ret; } + ret = -EPERM; switch (iface_param->param) { case ISCSI_NET_PARAM_VLAN_ENABLED: + ret = 0; if (iface_param->value[0] != ISCSI_VLAN_ENABLE) - ret = mgmt_set_vlan(phba, BEISCSI_VLAN_DISABLE); + ret = beiscsi_if_set_vlan(phba, BEISCSI_VLAN_DISABLE); break; case ISCSI_NET_PARAM_VLAN_TAG: - ret = mgmt_set_vlan(phba, - *((uint16_t *)iface_param->value)); + ret = beiscsi_if_set_vlan(phba, + *((uint16_t *)iface_param->value)); break; - default: - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BS_%d : Unknown Param Type : %d\n", - iface_param->param); - return -ENOSYS; } return ret; } @@ -356,7 +353,7 @@ beiscsi_iface_config_ipv4(struct Scsi_Host *shost, struct beiscsi_hba *phba = iscsi_host_priv(shost); u8 *ip = NULL, *subnet = NULL, *gw; struct nlattr *nla; - int ret = 0; + int ret = -EPERM; /* Check the param */ switch (info->param) { @@ -405,14 +402,6 @@ beiscsi_iface_config_ipv4(struct Scsi_Host *shost, } ret = beiscsi_if_en_static(phba, BE2_IPV4, ip, subnet); break; - case ISCSI_NET_PARAM_VLAN_ENABLED: - case ISCSI_NET_PARAM_VLAN_TAG: - ret = beiscsi_set_vlan_tag(shost, info); - break; - default: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Param %d not supported\n", - info->param); } return ret; @@ -424,7 +413,7 @@ beiscsi_iface_config_ipv6(struct Scsi_Host *shost, void *data, uint32_t dt_len) { struct beiscsi_hba *phba = iscsi_host_priv(shost); - int ret = 0; + int ret = -EPERM; switch (iface_param->param) { case ISCSI_NET_PARAM_IFACE_ENABLE: @@ -439,14 +428,6 @@ beiscsi_iface_config_ipv6(struct Scsi_Host *shost, ret = beiscsi_if_en_static(phba, BE2_IPV6, iface_param->value, NULL); break; - case ISCSI_NET_PARAM_VLAN_ENABLED: - case ISCSI_NET_PARAM_VLAN_TAG: - ret = beiscsi_set_vlan_tag(shost, iface_param); - break; - default: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Param %d not supported\n", - iface_param->param); } return ret; @@ -485,24 +466,42 @@ int be2iscsi_iface_set_param(struct Scsi_Host *shost, return -EINVAL; } - switch (iface_param->iface_type) { - case ISCSI_IFACE_TYPE_IPV4: - ret = beiscsi_iface_config_ipv4(shost, iface_param, - data, dt_len); - break; - case ISCSI_IFACE_TYPE_IPV6: - ret = beiscsi_iface_config_ipv6(shost, iface_param, - data, dt_len); + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : %s.0 set param %d", + (iface_param->iface_type == ISCSI_IFACE_TYPE_IPV4) ? + "ipv4" : "ipv6", iface_param->param); + + ret = -EPERM; + switch (iface_param->param) { + case ISCSI_NET_PARAM_VLAN_ENABLED: + case ISCSI_NET_PARAM_VLAN_TAG: + ret = beiscsi_iface_config_vlan(shost, iface_param); break; default: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Invalid iface type :%d passed\n", - iface_param->iface_type); - break; + switch (iface_param->iface_type) { + case ISCSI_IFACE_TYPE_IPV4: + ret = beiscsi_iface_config_ipv4(shost, + iface_param, + data, dt_len); + break; + case ISCSI_IFACE_TYPE_IPV6: + ret = beiscsi_iface_config_ipv6(shost, + iface_param, + data, dt_len); + break; + } } + if (ret == -EPERM) { + __beiscsi_log(phba, KERN_ERR, + "BS_%d : %s.0 set param %d not permitted", + (iface_param->iface_type == + ISCSI_IFACE_TYPE_IPV4) ? "ipv4" : "ipv6", + iface_param->param); + ret = 0; + } if (ret) - return ret; + break; } return ret; @@ -548,16 +547,16 @@ static int be2iscsi_get_if_param(struct beiscsi_hba *phba, len = -EINVAL; else len = sprintf(buf, "%d\n", - (if_info->vlan_priority & - ISCSI_MAX_VLAN_ID)); + (if_info->vlan_priority & + ISCSI_MAX_VLAN_ID)); break; case ISCSI_NET_PARAM_VLAN_PRIORITY: if (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) len = -EINVAL; else len = sprintf(buf, "%d\n", - ((if_info->vlan_priority >> 13) & - ISCSI_MAX_VLAN_PRIORITY)); + ((if_info->vlan_priority >> 13) & + ISCSI_MAX_VLAN_PRIORITY)); break; default: WARN_ON(1); diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 2a5d65e27c14..5643cf16c08d 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -1001,6 +1001,9 @@ static int mgmt_alloc_cmd_data(struct beiscsi_hba *phba, struct be_dma_mem *cmd, } cmd->size = size; be_cmd_hdr_prepare(cmd->va, CMD_SUBSYSTEM_ISCSI, iscsi_cmd, size); + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BG_%d : subsystem iSCSI cmd %d size %d\n", + iscsi_cmd, size); return 0; } @@ -1259,6 +1262,42 @@ exit: return rc; } +/** + * beiscsi_if_set_vlan()- Issue and wait for CMD completion + * @phba: device private structure instance + * @vlan_tag: VLAN tag + * + * Issue the MBX Cmd and wait for the completion of the + * command. + * + * returns + * Success: 0 + * Failure: Non-Xero Value + **/ +int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag) +{ + int rc; + unsigned int tag; + + tag = be_cmd_set_vlan(phba, vlan_tag); + if (!tag) { + beiscsi_log(phba, KERN_ERR, + (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), + "BG_%d : VLAN Setting Failed\n"); + return -EBUSY; + } + + rc = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); + if (rc) { + beiscsi_log(phba, KERN_ERR, + (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), + "BS_%d : VLAN MBX Cmd Failed\n"); + return rc; + } + return rc; +} + + int mgmt_get_if_info(struct beiscsi_hba *phba, int ip_type, struct be_cmd_get_if_info_resp **if_info) { @@ -1447,42 +1486,6 @@ int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba, return -ENXIO; } -/** - * mgmt_set_vlan()- Issue and wait for CMD completion - * @phba: device private structure instance - * @vlan_tag: VLAN tag - * - * Issue the MBX Cmd and wait for the completion of the - * command. - * - * returns - * Success: 0 - * Failure: Non-Xero Value - **/ -int mgmt_set_vlan(struct beiscsi_hba *phba, - uint16_t vlan_tag) -{ - int rc; - unsigned int tag; - - tag = be_cmd_set_vlan(phba, vlan_tag); - if (!tag) { - beiscsi_log(phba, KERN_ERR, - (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), - "BG_%d : VLAN Setting Failed\n"); - return -EBUSY; - } - - rc = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); - if (rc) { - beiscsi_log(phba, KERN_ERR, - (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), - "BS_%d : VLAN MBX Cmd Failed\n"); - return rc; - } - return rc; -} - /** * beiscsi_drvr_ver_disp()- Display the driver Name and Version * @dev: ptr to device not used. diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index 71a96097f83a..7637b9e5e7e5 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -308,7 +308,7 @@ int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba, unsigned int mgmt_get_all_if_id(struct beiscsi_hba *phba); -int mgmt_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); +int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); ssize_t beiscsi_drvr_ver_disp(struct device *dev, struct device_attribute *attr, char *buf); From c5bf88897afecf015d9d8599d7ce3e5c3d01a0a7 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:05 +0530 Subject: [PATCH 036/173] scsi: be2iscsi: Update iface handle before any set param Move mgmt_get_all_if_id before any set param operation. Rename mgmt_get_all_if_id to beiscsi_if_get_handle. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_iscsi.c | 29 +++++------- drivers/scsi/be2iscsi/be_main.c | 30 +++--------- drivers/scsi/be2iscsi/be_mgmt.c | 81 ++++++++++++++++---------------- drivers/scsi/be2iscsi/be_mgmt.h | 2 +- 4 files changed, 61 insertions(+), 81 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index b725536e6c9e..1418d6bfa78b 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -319,17 +319,8 @@ beiscsi_iface_config_vlan(struct Scsi_Host *shost, struct iscsi_iface_param_info *iface_param) { struct beiscsi_hba *phba = iscsi_host_priv(shost); - int ret; + int ret = -EPERM; - /* Get the Interface Handle */ - ret = mgmt_get_all_if_id(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Getting Interface Handle Failed\n"); - return ret; - } - - ret = -EPERM; switch (iface_param->param) { case ISCSI_NET_PARAM_VLAN_ENABLED: ret = 0; @@ -440,7 +431,7 @@ int be2iscsi_iface_set_param(struct Scsi_Host *shost, struct beiscsi_hba *phba = iscsi_host_priv(shost); struct nlattr *attrib; uint32_t rm_len = dt_len; - int ret = 0 ; + int ret; if (phba->state & BE_ADAPTER_PCI_ERR) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, @@ -448,6 +439,14 @@ int be2iscsi_iface_set_param(struct Scsi_Host *shost, return -EBUSY; } + /* update interface_handle */ + ret = beiscsi_if_get_handle(phba); + if (ret) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, + "BS_%d : Getting Interface Handle Failed\n"); + return ret; + } + nla_for_each_attr(attrib, data, dt_len, rm_len) { iface_param = nla_data(attrib); @@ -573,7 +572,7 @@ int be2iscsi_iface_get_param(struct iscsi_iface *iface, struct Scsi_Host *shost = iscsi_iface_to_shost(iface); struct beiscsi_hba *phba = iscsi_host_priv(shost); struct be_cmd_get_def_gateway_resp gateway; - int len = -ENOSYS; + int len = -EPERM; if (phba->state & BE_ADAPTER_PCI_ERR) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, @@ -605,8 +604,6 @@ int be2iscsi_iface_get_param(struct iscsi_iface *iface, if (!len) len = sprintf(buf, "%pI4\n", &gateway.ip_addr.addr); break; - default: - len = -ENOSYS; } return len; @@ -624,7 +621,7 @@ int beiscsi_ep_get_param(struct iscsi_endpoint *ep, enum iscsi_param param, char *buf) { struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; - int len = 0; + int len; beiscsi_log(beiscsi_ep->phba, KERN_INFO, BEISCSI_LOG_CONFIG, @@ -642,7 +639,7 @@ int beiscsi_ep_get_param(struct iscsi_endpoint *ep, len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr); break; default: - return -ENOSYS; + len = -EPERM; } return len; } diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index f05e7737107d..01052d365d36 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -380,7 +380,7 @@ static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf) struct mgmt_session_info *boot_sess = &phba->boot_sess; struct mgmt_conn_info *boot_conn = &boot_sess->conn_list[0]; char *str = buf; - int rc; + int rc = -EPERM; switch (type) { case ISCSI_BOOT_TGT_NAME: @@ -434,9 +434,6 @@ static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf) case ISCSI_BOOT_TGT_NIC_ASSOC: rc = sprintf(str, "0\n"); break; - default: - rc = -ENOSYS; - break; } return rc; } @@ -445,15 +442,12 @@ static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf) { struct beiscsi_hba *phba = data; char *str = buf; - int rc; + int rc = -EPERM; switch (type) { case ISCSI_BOOT_INI_INITIATOR_NAME: rc = sprintf(str, "%s\n", phba->boot_sess.initiator_iscsiname); break; - default: - rc = -ENOSYS; - break; } return rc; } @@ -462,7 +456,7 @@ static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf) { struct beiscsi_hba *phba = data; char *str = buf; - int rc; + int rc = -EPERM; switch (type) { case ISCSI_BOOT_ETH_FLAGS: @@ -474,9 +468,6 @@ static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf) case ISCSI_BOOT_ETH_MAC: rc = beiscsi_get_macaddr(str, phba); break; - default: - rc = -ENOSYS; - break; } return rc; } @@ -484,7 +475,7 @@ static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf) static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type) { - umode_t rc; + umode_t rc = 0; switch (type) { case ISCSI_BOOT_TGT_NAME: @@ -498,24 +489,18 @@ static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type) case ISCSI_BOOT_TGT_FLAGS: rc = S_IRUGO; break; - default: - rc = 0; - break; } return rc; } static umode_t beiscsi_ini_get_attr_visibility(void *data, int type) { - umode_t rc; + umode_t rc = 0; switch (type) { case ISCSI_BOOT_INI_INITIATOR_NAME: rc = S_IRUGO; break; - default: - rc = 0; - break; } return rc; } @@ -523,7 +508,7 @@ static umode_t beiscsi_ini_get_attr_visibility(void *data, int type) static umode_t beiscsi_eth_get_attr_visibility(void *data, int type) { - umode_t rc; + umode_t rc = 0; switch (type) { case ISCSI_BOOT_ETH_FLAGS: @@ -531,9 +516,6 @@ static umode_t beiscsi_eth_get_attr_visibility(void *data, int type) case ISCSI_BOOT_ETH_INDEX: rc = S_IRUGO; break; - default: - rc = 0; - break; } return rc; } diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 5643cf16c08d..fa95525c11b3 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -609,7 +609,7 @@ unsigned int mgmt_vendor_specific_fw_cmd(struct be_ctrl_info *ctrl, bsg_req->rqst_data.h_vendor.vendor_cmd[0]); mutex_unlock(&ctrl->mbox_lock); - return -ENOSYS; + return -EPERM; } wrb = alloc_mcc_wrb(phba, &tag); @@ -892,44 +892,6 @@ int mgmt_open_connection(struct beiscsi_hba *phba, return tag; } -unsigned int mgmt_get_all_if_id(struct beiscsi_hba *phba) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct be_cmd_get_all_if_id_req *req; - struct be_cmd_get_all_if_id_req *pbe_allid; - unsigned int tag; - int status = 0; - - if (mutex_lock_interruptible(&ctrl->mbox_lock)) - return -EINTR; - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return -ENOMEM; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, - OPCODE_COMMON_ISCSI_NTWK_GET_ALL_IF_ID, - sizeof(*req)); - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - - status = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); - if (status) { - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed in mgmt_get_all_if_id\n"); - return -EBUSY; - } - - pbe_allid = embedded_payload(wrb); - phba->interface_handle = pbe_allid->if_hndl_list[0]; - - return status; -} - /* * mgmt_exec_nonemb_cmd()- Execute Non Embedded MBX Cmd * @phba: Driver priv structure @@ -1007,6 +969,45 @@ static int mgmt_alloc_cmd_data(struct beiscsi_hba *phba, struct be_dma_mem *cmd, return 0; } +unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb; + struct be_cmd_get_all_if_id_req *req; + struct be_cmd_get_all_if_id_req *pbe_allid; + unsigned int tag; + int status = 0; + + if (mutex_lock_interruptible(&ctrl->mbox_lock)) + return -EINTR; + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return -ENOMEM; + } + + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_NTWK_GET_ALL_IF_ID, + sizeof(*req)); + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + + status = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); + if (status) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : %s failed: %d\n", __func__, status); + return -EBUSY; + } + + pbe_allid = embedded_payload(wrb); + /* we now support only one interface per function */ + phba->interface_handle = pbe_allid->if_hndl_list[0]; + + return status; +} + static int beiscsi_if_mod_gw(struct beiscsi_hba *phba, u32 action, u32 ip_type, u8 *gw) { @@ -1306,7 +1307,7 @@ int mgmt_get_if_info(struct beiscsi_hba *phba, int ip_type, uint32_t ioctl_size = sizeof(struct be_cmd_get_if_info_resp); int rc; - rc = mgmt_get_all_if_id(phba); + rc = beiscsi_if_get_handle(phba); if (rc) return rc; diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index 7637b9e5e7e5..8ae6a2418c44 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -306,7 +306,7 @@ int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw); int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba, unsigned int *s_handle); -unsigned int mgmt_get_all_if_id(struct beiscsi_hba *phba); +unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba); int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); From 96b48b9292a234e90e812a063f552e76672135b5 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:06 +0530 Subject: [PATCH 037/173] scsi: be2iscsi: Rename iface get/set/create/destroy APIs Rename mgmt_get_if_info to be consistent with APIs name. Rename create/destroy APIs to indicate IFACE operations. Remove legacy be2iscsi and use beiscsi. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_iscsi.c | 42 ++++++++++++++++---------------- drivers/scsi/be2iscsi/be_iscsi.h | 10 ++++---- drivers/scsi/be2iscsi/be_main.c | 10 ++++---- drivers/scsi/be2iscsi/be_mgmt.c | 8 +++--- drivers/scsi/be2iscsi/be_mgmt.h | 4 +-- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 1418d6bfa78b..1fe273db5853 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -237,7 +237,7 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid); } -static int beiscsi_create_ipv4_iface(struct beiscsi_hba *phba) +static int beiscsi_iface_create_ipv4(struct beiscsi_hba *phba) { if (phba->ipv4_iface) return 0; @@ -256,7 +256,7 @@ static int beiscsi_create_ipv4_iface(struct beiscsi_hba *phba) return 0; } -static int beiscsi_create_ipv6_iface(struct beiscsi_hba *phba) +static int beiscsi_iface_create_ipv6(struct beiscsi_hba *phba) { if (phba->ipv6_iface) return 0; @@ -275,22 +275,22 @@ static int beiscsi_create_ipv6_iface(struct beiscsi_hba *phba) return 0; } -void beiscsi_create_def_ifaces(struct beiscsi_hba *phba) +void beiscsi_iface_create_default(struct beiscsi_hba *phba) { struct be_cmd_get_if_info_resp *if_info; - if (!mgmt_get_if_info(phba, BE2_IPV4, &if_info)) { - beiscsi_create_ipv4_iface(phba); + if (!beiscsi_if_get_info(phba, BE2_IPV4, &if_info)) { + beiscsi_iface_create_ipv4(phba); kfree(if_info); } - if (!mgmt_get_if_info(phba, BE2_IPV6, &if_info)) { - beiscsi_create_ipv6_iface(phba); + if (!beiscsi_if_get_info(phba, BE2_IPV6, &if_info)) { + beiscsi_iface_create_ipv6(phba); kfree(if_info); } } -void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba) +void beiscsi_iface_destroy_default(struct beiscsi_hba *phba) { if (phba->ipv6_iface) { iscsi_destroy_iface(phba->ipv6_iface); @@ -350,7 +350,7 @@ beiscsi_iface_config_ipv4(struct Scsi_Host *shost, switch (info->param) { case ISCSI_NET_PARAM_IFACE_ENABLE: if (info->value[0] == ISCSI_IFACE_ENABLE) - ret = beiscsi_create_ipv4_iface(phba); + ret = beiscsi_iface_create_ipv4(phba); else { iscsi_destroy_iface(phba->ipv4_iface); phba->ipv4_iface = NULL; @@ -409,7 +409,7 @@ beiscsi_iface_config_ipv6(struct Scsi_Host *shost, switch (iface_param->param) { case ISCSI_NET_PARAM_IFACE_ENABLE: if (iface_param->value[0] == ISCSI_IFACE_ENABLE) - ret = beiscsi_create_ipv6_iface(phba); + ret = beiscsi_iface_create_ipv6(phba); else { iscsi_destroy_iface(phba->ipv6_iface); phba->ipv6_iface = NULL; @@ -424,8 +424,8 @@ beiscsi_iface_config_ipv6(struct Scsi_Host *shost, return ret; } -int be2iscsi_iface_set_param(struct Scsi_Host *shost, - void *data, uint32_t dt_len) +int beiscsi_iface_set_param(struct Scsi_Host *shost, + void *data, uint32_t dt_len) { struct iscsi_iface_param_info *iface_param = NULL; struct beiscsi_hba *phba = iscsi_host_priv(shost); @@ -506,9 +506,9 @@ int be2iscsi_iface_set_param(struct Scsi_Host *shost, return ret; } -static int be2iscsi_get_if_param(struct beiscsi_hba *phba, - struct iscsi_iface *iface, int param, - char *buf) +static int __beiscsi_iface_get_param(struct beiscsi_hba *phba, + struct iscsi_iface *iface, + int param, char *buf) { struct be_cmd_get_if_info_resp *if_info; int len, ip_type = BE2_IPV4; @@ -516,7 +516,7 @@ static int be2iscsi_get_if_param(struct beiscsi_hba *phba, if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) ip_type = BE2_IPV6; - len = mgmt_get_if_info(phba, ip_type, &if_info); + len = beiscsi_if_get_info(phba, ip_type, &if_info); if (len) return len; @@ -565,9 +565,9 @@ static int be2iscsi_get_if_param(struct beiscsi_hba *phba, return len; } -int be2iscsi_iface_get_param(struct iscsi_iface *iface, - enum iscsi_param_type param_type, - int param, char *buf) +int beiscsi_iface_get_param(struct iscsi_iface *iface, + enum iscsi_param_type param_type, + int param, char *buf) { struct Scsi_Host *shost = iscsi_iface_to_shost(iface); struct beiscsi_hba *phba = iscsi_host_priv(shost); @@ -588,7 +588,7 @@ int be2iscsi_iface_get_param(struct iscsi_iface *iface, case ISCSI_NET_PARAM_VLAN_ENABLED: case ISCSI_NET_PARAM_VLAN_ID: case ISCSI_NET_PARAM_VLAN_PRIORITY: - len = be2iscsi_get_if_param(phba, iface, param, buf); + len = __beiscsi_iface_get_param(phba, iface, param, buf); break; case ISCSI_NET_PARAM_IFACE_ENABLE: if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) @@ -1360,7 +1360,7 @@ free_ep: iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep); } -umode_t be2iscsi_attr_is_visible(int param_type, int param) +umode_t beiscsi_attr_is_visible(int param_type, int param) { switch (param_type) { case ISCSI_NET_PARAM: diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h index 0c84e1c0763a..5928ba9d8918 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.h +++ b/drivers/scsi/be2iscsi/be_iscsi.h @@ -30,18 +30,18 @@ #define NON_BLOCKING 0x0 #define BLOCKING 0x1 -void beiscsi_create_def_ifaces(struct beiscsi_hba *phba); +void beiscsi_iface_create_default(struct beiscsi_hba *phba); -void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba); +void beiscsi_iface_destroy_default(struct beiscsi_hba *phba); -int be2iscsi_iface_get_param(struct iscsi_iface *iface, +int beiscsi_iface_get_param(struct iscsi_iface *iface, enum iscsi_param_type param_type, int param, char *buf); -int be2iscsi_iface_set_param(struct Scsi_Host *shost, +int beiscsi_iface_set_param(struct Scsi_Host *shost, void *data, uint32_t count); -umode_t be2iscsi_attr_is_visible(int param_type, int param); +umode_t beiscsi_attr_is_visible(int param_type, int param); void beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, struct beiscsi_offload_params *params); diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 01052d365d36..71c91314d4c9 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -5327,7 +5327,7 @@ static void beiscsi_remove(struct pci_dev *pcidev) return; } - beiscsi_destroy_def_ifaces(phba); + beiscsi_iface_destroy_default(phba); iscsi_boot_destroy_kset(phba->boot_kset); iscsi_host_remove(phba->shost); beiscsi_quiesce(phba, BEISCSI_CLEAN_UNLOAD); @@ -5777,7 +5777,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, "BM_%d : Could not set up " "iSCSI boot info.\n"); - beiscsi_create_def_ifaces(phba); + beiscsi_iface_create_default(phba); schedule_delayed_work(&phba->beiscsi_hw_check_task, msecs_to_jiffies(1000)); @@ -5828,9 +5828,9 @@ struct iscsi_transport beiscsi_iscsi_transport = { .create_conn = beiscsi_conn_create, .bind_conn = beiscsi_conn_bind, .destroy_conn = iscsi_conn_teardown, - .attr_is_visible = be2iscsi_attr_is_visible, - .set_iface_param = be2iscsi_iface_set_param, - .get_iface_param = be2iscsi_iface_get_param, + .attr_is_visible = beiscsi_attr_is_visible, + .set_iface_param = beiscsi_iface_set_param, + .get_iface_param = beiscsi_iface_get_param, .set_param = beiscsi_set_param, .get_conn_param = iscsi_conn_get_param, .get_session_param = iscsi_session_get_param, diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index fa95525c11b3..8d05add812c4 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -1167,7 +1167,7 @@ int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, struct be_dma_mem nonemb_cmd; int rc; - rc = mgmt_get_if_info(phba, ip_type, &if_info); + rc = beiscsi_if_get_info(phba, ip_type, &if_info); if (rc) return rc; @@ -1212,7 +1212,7 @@ int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type) u8 *gw; int rc; - rc = mgmt_get_if_info(phba, ip_type, &if_info); + rc = beiscsi_if_get_info(phba, ip_type, &if_info); if (rc) return rc; @@ -1299,8 +1299,8 @@ int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag) } -int mgmt_get_if_info(struct beiscsi_hba *phba, int ip_type, - struct be_cmd_get_if_info_resp **if_info) +int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type, + struct be_cmd_get_if_info_resp **if_info) { struct be_cmd_get_if_info_req *req; struct be_dma_mem nonemb_cmd; diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index 8ae6a2418c44..294b740805a6 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -295,8 +295,8 @@ unsigned int mgmt_get_session_info(struct beiscsi_hba *phba, int mgmt_get_nic_conf(struct beiscsi_hba *phba, struct be_cmd_get_nic_conf_resp *mac); -int mgmt_get_if_info(struct beiscsi_hba *phba, int ip_type, - struct be_cmd_get_if_info_resp **if_info); +int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type, + struct be_cmd_get_if_info_resp **if_info); int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, struct be_cmd_get_def_gateway_resp *resp); From d8383b34e34ce32251317e15f8af3eda874c8ece Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:07 +0530 Subject: [PATCH 038/173] scsi: be2iscsi: Handle only NET_PARAM in iface_get_param Wrong settings displayed for iface: iface.header_digest = 192.168.197.22 iface.data_digest = 255.255.255.0 iface.immediate_data = 192.168.197.1 Process ISCSI_NET_PARAM only in beiscsi_iface_get_param. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_iscsi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 1fe273db5853..fa415c41ba04 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -574,6 +574,8 @@ int beiscsi_iface_get_param(struct iscsi_iface *iface, struct be_cmd_get_def_gateway_resp gateway; int len = -EPERM; + if (param_type != ISCSI_NET_PARAM) + return 0; if (phba->state & BE_ADAPTER_PCI_ERR) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : In PCI_ERROR Recovery\n"); From 290aa376a6669d4cec22d9db5695f0036e218132 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:08 +0530 Subject: [PATCH 039/173] scsi: be2iscsi: Check all zeroes IP before issuing IOCTL Redefine FW IP types. Before issuing IOCTL to clear IP, check if IP is all zeroes. All zeroes IP implies IP is not set in FW so FW fails that IOCTL. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.h | 8 ++++ drivers/scsi/be2iscsi/be_iscsi.c | 27 ++++++------ drivers/scsi/be2iscsi/be_iscsi.h | 7 ---- drivers/scsi/be2iscsi/be_main.c | 2 +- drivers/scsi/be2iscsi/be_mgmt.c | 72 ++++++++++++++++++++------------ 5 files changed, 69 insertions(+), 47 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index deeb951e6874..a4bc83cbec9e 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -365,6 +365,14 @@ struct ip_addr_format { u16 size_of_structure; u8 reserved; u8 ip_type; +#define BEISCSI_IP_TYPE_V4 0x1 +#define BEISCSI_IP_TYPE_STATIC_V4 0x3 +#define BEISCSI_IP_TYPE_DHCP_V4 0x5 +/* type v4 values < type v6 values */ +#define BEISCSI_IP_TYPE_V6 0x10 +#define BEISCSI_IP_TYPE_ROUTABLE_V6 0x30 +#define BEISCSI_IP_TYPE_LINK_LOCAL_V6 0x50 +#define BEISCSI_IP_TYPE_AUTO_V6 0x90 u8 addr[16]; u32 rsvd0; } __packed; diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index fa415c41ba04..faa37f67a8e6 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -279,12 +279,12 @@ void beiscsi_iface_create_default(struct beiscsi_hba *phba) { struct be_cmd_get_if_info_resp *if_info; - if (!beiscsi_if_get_info(phba, BE2_IPV4, &if_info)) { + if (!beiscsi_if_get_info(phba, BEISCSI_IP_TYPE_V4, &if_info)) { beiscsi_iface_create_ipv4(phba); kfree(if_info); } - if (!beiscsi_if_get_info(phba, BE2_IPV6, &if_info)) { + if (!beiscsi_if_get_info(phba, BEISCSI_IP_TYPE_V6, &if_info)) { beiscsi_iface_create_ipv6(phba); kfree(if_info); } @@ -358,14 +358,15 @@ beiscsi_iface_config_ipv4(struct Scsi_Host *shost, break; case ISCSI_NET_PARAM_IPV4_GW: gw = info->value; - ret = beiscsi_if_set_gw(phba, BE2_IPV4, gw); + ret = beiscsi_if_set_gw(phba, BEISCSI_IP_TYPE_V4, gw); break; case ISCSI_NET_PARAM_IPV4_BOOTPROTO: if (info->value[0] == ISCSI_BOOTPROTO_DHCP) - ret = beiscsi_if_en_dhcp(phba, BE2_IPV4); + ret = beiscsi_if_en_dhcp(phba, BEISCSI_IP_TYPE_V4); else if (info->value[0] == ISCSI_BOOTPROTO_STATIC) /* release DHCP IP address */ - ret = beiscsi_if_en_static(phba, BE2_IPV4, NULL, NULL); + ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4, + NULL, NULL); else beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : Invalid BOOTPROTO: %d\n", @@ -378,7 +379,8 @@ beiscsi_iface_config_ipv4(struct Scsi_Host *shost, info = nla_data(nla); subnet = info->value; } - ret = beiscsi_if_en_static(phba, BE2_IPV4, ip, subnet); + ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4, + ip, subnet); break; case ISCSI_NET_PARAM_IPV4_SUBNET: /* @@ -391,7 +393,8 @@ beiscsi_iface_config_ipv4(struct Scsi_Host *shost, info = nla_data(nla); ip = info->value; } - ret = beiscsi_if_en_static(phba, BE2_IPV4, ip, subnet); + ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4, + ip, subnet); break; } @@ -416,7 +419,7 @@ beiscsi_iface_config_ipv6(struct Scsi_Host *shost, } break; case ISCSI_NET_PARAM_IPV6_ADDR: - ret = beiscsi_if_en_static(phba, BE2_IPV6, + ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V6, iface_param->value, NULL); break; } @@ -511,10 +514,10 @@ static int __beiscsi_iface_get_param(struct beiscsi_hba *phba, int param, char *buf) { struct be_cmd_get_if_info_resp *if_info; - int len, ip_type = BE2_IPV4; + int len, ip_type = BEISCSI_IP_TYPE_V4; if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) - ip_type = BE2_IPV6; + ip_type = BEISCSI_IP_TYPE_V6; len = beiscsi_if_get_info(phba, ip_type, &if_info); if (len) @@ -602,7 +605,7 @@ int beiscsi_iface_get_param(struct iscsi_iface *iface, break; case ISCSI_NET_PARAM_IPV4_GW: memset(&gateway, 0, sizeof(gateway)); - len = beiscsi_if_get_gw(phba, BE2_IPV4, &gateway); + len = beiscsi_if_get_gw(phba, BEISCSI_IP_TYPE_V4, &gateway); if (!len) len = sprintf(buf, "%pI4\n", &gateway.ip_addr.addr); break; @@ -635,7 +638,7 @@ int beiscsi_ep_get_param(struct iscsi_endpoint *ep, len = sprintf(buf, "%hu\n", beiscsi_ep->dst_tcpport); break; case ISCSI_PARAM_CONN_ADDRESS: - if (beiscsi_ep->ip_type == BE2_IPV4) + if (beiscsi_ep->ip_type == BEISCSI_IP_TYPE_V4) len = sprintf(buf, "%pI4\n", &beiscsi_ep->dst_addr); else len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr); diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h index 5928ba9d8918..0089e67badf8 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.h +++ b/drivers/scsi/be2iscsi/be_iscsi.h @@ -23,13 +23,6 @@ #include "be_main.h" #include "be_mgmt.h" -#define BE2_IPV4 0x1 -#define BE2_IPV6 0x10 -#define BE2_DHCP_V4 0x05 - -#define NON_BLOCKING 0x0 -#define BLOCKING 0x1 - void beiscsi_iface_create_default(struct beiscsi_hba *phba); void beiscsi_iface_destroy_default(struct beiscsi_hba *phba); diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 71c91314d4c9..0fbb80d3e090 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -389,7 +389,7 @@ static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf) (char *)&boot_sess->target_name); break; case ISCSI_BOOT_TGT_IP_ADDR: - if (boot_conn->dest_ipaddr.ip_type == 0x1) + if (boot_conn->dest_ipaddr.ip_type == BEISCSI_IP_TYPE_V4) rc = sprintf(buf, "%pI4\n", (char *)&boot_conn->dest_ipaddr.addr); else diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 8d05add812c4..8069ef0ce533 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -844,7 +844,7 @@ int mgmt_open_connection(struct beiscsi_hba *phba, nonemb_cmd->size); if (dst_addr->sa_family == PF_INET) { __be32 s_addr = daddr_in->sin_addr.s_addr; - req->ip_address.ip_type = BE2_IPV4; + req->ip_address.ip_type = BEISCSI_IP_TYPE_V4; req->ip_address.addr[0] = s_addr & 0x000000ff; req->ip_address.addr[1] = (s_addr & 0x0000ff00) >> 8; req->ip_address.addr[2] = (s_addr & 0x00ff0000) >> 16; @@ -852,17 +852,17 @@ int mgmt_open_connection(struct beiscsi_hba *phba, req->tcp_port = ntohs(daddr_in->sin_port); beiscsi_ep->dst_addr = daddr_in->sin_addr.s_addr; beiscsi_ep->dst_tcpport = ntohs(daddr_in->sin_port); - beiscsi_ep->ip_type = BE2_IPV4; + beiscsi_ep->ip_type = BEISCSI_IP_TYPE_V4; } else { /* else its PF_INET6 family */ - req->ip_address.ip_type = BE2_IPV6; + req->ip_address.ip_type = BEISCSI_IP_TYPE_V6; memcpy(&req->ip_address.addr, &daddr_in6->sin6_addr.in6_u.u6_addr8, 16); req->tcp_port = ntohs(daddr_in6->sin6_port); beiscsi_ep->dst_tcpport = ntohs(daddr_in6->sin6_port); memcpy(&beiscsi_ep->dst6_addr, &daddr_in6->sin6_addr.in6_u.u6_addr8, 16); - beiscsi_ep->ip_type = BE2_IPV6; + beiscsi_ep->ip_type = BEISCSI_IP_TYPE_V6; } req->cid = cid; i = phba->nxt_cqid++; @@ -1008,6 +1008,16 @@ unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba) return status; } +static inline bool beiscsi_if_zero_ip(u8 *ip, u32 ip_type) +{ + u32 len; + + len = (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN; + while (len && !ip[len - 1]) + len--; + return (len == 0); +} + static int beiscsi_if_mod_gw(struct beiscsi_hba *phba, u32 action, u32 ip_type, u8 *gw) { @@ -1025,7 +1035,7 @@ static int beiscsi_if_mod_gw(struct beiscsi_hba *phba, req->action = action; req->ip_addr.ip_type = ip_type; memcpy(req->ip_addr.addr, gw, - (ip_type == BE2_IPV4) ? IP_V4_LEN : IP_V6_LEN); + (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN); return mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); } @@ -1042,12 +1052,14 @@ int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw) return rt_val; } - rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, ip_type, - gw_resp.ip_addr.addr); - if (rt_val) { - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to clear Gateway Addr Set\n"); - return rt_val; + if (!beiscsi_if_zero_ip(gw_resp.ip_addr.addr, ip_type)) { + rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, ip_type, + gw_resp.ip_addr.addr); + if (rt_val) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to clear Gateway Addr Set\n"); + return rt_val; + } } rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_ADD, ip_type, gw); @@ -1138,7 +1150,7 @@ beiscsi_if_set_ip(struct beiscsi_hba *phba, u8 *ip, req->ip_params.ip_record.ip_addr.size_of_structure = sizeof(struct be_ip_addr_subnet_format); req->ip_params.ip_record.ip_addr.ip_type = ip_type; - ip_len = ip_type == BE2_IPV4 ? IP_V4_LEN : IP_V6_LEN; + ip_len = (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN; memcpy(req->ip_params.ip_record.ip_addr.addr, ip, ip_len); if (subnet) memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, @@ -1190,10 +1202,12 @@ int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, } } - /* first delete any old IP set */ - rc = beiscsi_if_clr_ip(phba, if_info); - if (rc) - goto exit; + /* first delete any IP set */ + if (!beiscsi_if_zero_ip(if_info->ip_addr.addr, ip_type)) { + rc = beiscsi_if_clr_ip(phba, if_info); + if (rc) + goto exit; + } /* if ip == NULL then this is called just to release DHCP IP */ if (ip) @@ -1222,10 +1236,12 @@ int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type) goto exit; } - /* first delete any old static IP set */ - rc = beiscsi_if_clr_ip(phba, if_info); - if (rc) - goto exit; + /* first delete any IP set */ + if (!beiscsi_if_zero_ip(if_info->ip_addr.addr, ip_type)) { + rc = beiscsi_if_clr_ip(phba, if_info); + if (rc) + goto exit; + } /* delete gateway settings if mode change is to DHCP */ memset(&gw_resp, 0, sizeof(gw_resp)); @@ -1237,12 +1253,14 @@ int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type) goto exit; } gw = (u8 *)&gw_resp.ip_addr.addr; - rc = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, - if_info->ip_addr.ip_type, gw); - if (rc) { - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to clear Gateway Addr Set\n"); - goto exit; + if (!beiscsi_if_zero_ip(gw, if_info->ip_addr.ip_type)) { + rc = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, + if_info->ip_addr.ip_type, gw); + if (rc) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to clear Gateway Addr Set\n"); + goto exit; + } } rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, @@ -1252,7 +1270,7 @@ int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type) goto exit; dhcpreq = nonemb_cmd.va; - dhcpreq->flags = BLOCKING; + dhcpreq->flags = 1; /* 1 - blocking; 0 - non-blocking */ dhcpreq->retry_count = 1; dhcpreq->interface_hndl = phba->interface_handle; dhcpreq->ip_type = ip_type; From 1f5e847acbda0c46f66a7571bd62a9970a248b27 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:09 +0530 Subject: [PATCH 040/173] scsi: be2iscsi: Remove alloc_mcc_tag & beiscsi_pci_soft_reset alloc_mcc_tag was replaced with alloc_mcc_wrb and is no more used. beiscsi_pci_soft_reset is not used at all and won't be needed. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 76 --------------------------------- drivers/scsi/be2iscsi/be_cmds.h | 1 - 2 files changed, 77 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 59b8e2dc3aca..403296239ae9 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -21,60 +21,6 @@ #include "be.h" #include "be_mgmt.h" -int beiscsi_pci_soft_reset(struct beiscsi_hba *phba) -{ - u32 sreset; - u8 *pci_reset_offset = 0; - u8 *pci_online0_offset = 0; - u8 *pci_online1_offset = 0; - u32 pconline0 = 0; - u32 pconline1 = 0; - u32 i; - - pci_reset_offset = (u8 *)phba->pci_va + BE2_SOFT_RESET; - pci_online0_offset = (u8 *)phba->pci_va + BE2_PCI_ONLINE0; - pci_online1_offset = (u8 *)phba->pci_va + BE2_PCI_ONLINE1; - sreset = readl((void *)pci_reset_offset); - sreset |= BE2_SET_RESET; - writel(sreset, (void *)pci_reset_offset); - - i = 0; - while (sreset & BE2_SET_RESET) { - if (i > 64) - break; - msleep(100); - sreset = readl((void *)pci_reset_offset); - i++; - } - - if (sreset & BE2_SET_RESET) { - printk(KERN_ERR DRV_NAME - " Soft Reset did not deassert\n"); - return -EIO; - } - pconline1 = BE2_MPU_IRAM_ONLINE; - writel(pconline0, (void *)pci_online0_offset); - writel(pconline1, (void *)pci_online1_offset); - - sreset |= BE2_SET_RESET; - writel(sreset, (void *)pci_reset_offset); - - i = 0; - while (sreset & BE2_SET_RESET) { - if (i > 64) - break; - msleep(1); - sreset = readl((void *)pci_reset_offset); - i++; - } - if (sreset & BE2_SET_RESET) { - printk(KERN_ERR DRV_NAME - " MPU Online Soft Reset did not deassert\n"); - return -EIO; - } - return 0; -} - int be_chk_reset_complete(struct beiscsi_hba *phba) { unsigned int num_loop; @@ -104,28 +50,6 @@ int be_chk_reset_complete(struct beiscsi_hba *phba) return 0; } -unsigned int alloc_mcc_tag(struct beiscsi_hba *phba) -{ - unsigned int tag = 0; - - spin_lock(&phba->ctrl.mcc_lock); - if (phba->ctrl.mcc_tag_available) { - tag = phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index]; - phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index] = 0; - phba->ctrl.mcc_tag_status[tag] = 0; - phba->ctrl.ptag_state[tag].tag_state = 0; - } - if (tag) { - phba->ctrl.mcc_tag_available--; - if (phba->ctrl.mcc_alloc_index == (MAX_MCC_CMD - 1)) - phba->ctrl.mcc_alloc_index = 0; - else - phba->ctrl.mcc_alloc_index++; - } - spin_unlock(&phba->ctrl.mcc_lock); - return tag; -} - struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, unsigned int *ref_tag) { diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index a4bc83cbec9e..c50b74afd00a 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -1376,7 +1376,6 @@ struct be_cmd_get_port_name { * the cxn */ -int beiscsi_pci_soft_reset(struct beiscsi_hba *phba); int be_chk_reset_complete(struct beiscsi_hba *phba); void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, From a30950161954a046421b26fbf55a873ae27b1e25 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:10 +0530 Subject: [PATCH 041/173] scsi: be2iscsi: Remove isr_lock and dead code todo_mcc_cq is not needed as only MCC work is queued. todo_cq is not used at all. Rename functions to be consistent. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be.h | 2 +- drivers/scsi/be2iscsi/be_main.c | 132 ++++++++++++-------------------- drivers/scsi/be2iscsi/be_main.h | 2 - 3 files changed, 49 insertions(+), 87 deletions(-) diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h index ee5ace873535..621291ab0214 100644 --- a/drivers/scsi/be2iscsi/be.h +++ b/drivers/scsi/be2iscsi/be.h @@ -100,7 +100,7 @@ struct be_eq_obj { struct be_queue_info q; struct beiscsi_hba *phba; struct be_queue_info *cq; - struct work_struct work_cqs; /* Work Item */ + struct work_struct mcc_work; /* Work Item */ struct irq_poll iopoll; }; diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 0fbb80d3e090..6179c4e512a6 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -832,12 +832,11 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba, static irqreturn_t be_isr_mcc(int irq, void *dev_id) { struct beiscsi_hba *phba; - struct be_eq_entry *eqe = NULL; + struct be_eq_entry *eqe; struct be_queue_info *eq; struct be_queue_info *mcc; - unsigned int num_eq_processed; + unsigned int mcc_events; struct be_eq_obj *pbe_eq; - unsigned long flags; pbe_eq = dev_id; eq = &pbe_eq->q; @@ -845,27 +844,23 @@ static irqreturn_t be_isr_mcc(int irq, void *dev_id) mcc = &phba->ctrl.mcc_obj.cq; eqe = queue_tail_node(eq); - num_eq_processed = 0; - + mcc_events = 0; while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { if (((eqe->dw[offsetof(struct amap_eq_entry, resource_id) / 32] & EQE_RESID_MASK) >> 16) == mcc->id) { - spin_lock_irqsave(&phba->isr_lock, flags); - pbe_eq->todo_mcc_cq = true; - spin_unlock_irqrestore(&phba->isr_lock, flags); + mcc_events++; } AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); eqe = queue_tail_node(eq); - num_eq_processed++; } - if (pbe_eq->todo_mcc_cq) - queue_work(phba->wq, &pbe_eq->work_cqs); - if (num_eq_processed) - hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1); + if (mcc_events) { + queue_work(phba->wq, &pbe_eq->mcc_work); + hwi_ring_eq_db(phba, eq->id, 1, mcc_events, 1, 1); + } return IRQ_HANDLED; } @@ -884,7 +879,6 @@ static irqreturn_t be_isr_msix(int irq, void *dev_id) eq = &pbe_eq->q; phba = pbe_eq->phba; - /* disable interrupt till iopoll completes */ hwi_ring_eq_db(phba, eq->id, 1, 0, 0, 1); irq_poll_sched(&pbe_eq->iopoll); @@ -902,14 +896,13 @@ static irqreturn_t be_isr(int irq, void *dev_id) struct beiscsi_hba *phba; struct hwi_controller *phwi_ctrlr; struct hwi_context_memory *phwi_context; - struct be_eq_entry *eqe = NULL; + struct be_eq_entry *eqe; struct be_queue_info *eq; struct be_queue_info *mcc; - unsigned long flags, index; - unsigned int num_mcceq_processed, num_ioeq_processed; + unsigned int mcc_events, io_events; struct be_ctrl_info *ctrl; struct be_eq_obj *pbe_eq; - int isr; + int isr, rearm; phba = dev_id; ctrl = &phba->ctrl; @@ -924,44 +917,35 @@ static irqreturn_t be_isr(int irq, void *dev_id) eq = &phwi_context->be_eq[0].q; mcc = &phba->ctrl.mcc_obj.cq; - index = 0; eqe = queue_tail_node(eq); - num_ioeq_processed = 0; - num_mcceq_processed = 0; + io_events = 0; + mcc_events = 0; while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { if (((eqe->dw[offsetof(struct amap_eq_entry, - resource_id) / 32] & - EQE_RESID_MASK) >> 16) == mcc->id) { - spin_lock_irqsave(&phba->isr_lock, flags); - pbe_eq->todo_mcc_cq = true; - spin_unlock_irqrestore(&phba->isr_lock, flags); - num_mcceq_processed++; - } else { - irq_poll_sched(&pbe_eq->iopoll); - num_ioeq_processed++; - } + resource_id) / 32] & EQE_RESID_MASK) >> 16) == mcc->id) + mcc_events++; + else + io_events++; AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); eqe = queue_tail_node(eq); } - if (num_ioeq_processed || num_mcceq_processed) { - if (pbe_eq->todo_mcc_cq) - queue_work(phba->wq, &pbe_eq->work_cqs); - - if ((num_mcceq_processed) && (!num_ioeq_processed)) - hwi_ring_eq_db(phba, eq->id, 0, - (num_ioeq_processed + - num_mcceq_processed) , 1, 1); - else - hwi_ring_eq_db(phba, eq->id, 0, - (num_ioeq_processed + - num_mcceq_processed), 0, 1); - - return IRQ_HANDLED; - } else + if (!io_events && !mcc_events) return IRQ_NONE; + + /* no need to rearm if interrupt is only for IOs */ + rearm = 0; + if (mcc_events) { + queue_work(phba->wq, &pbe_eq->mcc_work); + /* rearm for MCCQ */ + rearm = 1; + } + if (io_events) + irq_poll_sched(&pbe_eq->iopoll); + hwi_ring_eq_db(phba, eq->id, 0, (io_events + mcc_events), rearm, 1); + return IRQ_HANDLED; } @@ -2055,6 +2039,18 @@ void beiscsi_process_mcc_cq(struct beiscsi_hba *phba) hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 1); } +static void beiscsi_mcc_work(struct work_struct *work) +{ + struct be_eq_obj *pbe_eq; + struct beiscsi_hba *phba; + + pbe_eq = container_of(work, struct be_eq_obj, mcc_work); + phba = pbe_eq->phba; + beiscsi_process_mcc_cq(phba); + /* rearm EQ for further interrupts */ + hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); +} + /** * beiscsi_process_cq()- Process the Completion Queue * @pbe_eq: Event Q on which the Completion has come @@ -2244,46 +2240,15 @@ proc_next_cqe: return total; } -void beiscsi_process_all_cqs(struct work_struct *work) -{ - unsigned long flags; - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - struct beiscsi_hba *phba; - struct be_eq_obj *pbe_eq = - container_of(work, struct be_eq_obj, work_cqs); - - phba = pbe_eq->phba; - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - - if (pbe_eq->todo_mcc_cq) { - spin_lock_irqsave(&phba->isr_lock, flags); - pbe_eq->todo_mcc_cq = false; - spin_unlock_irqrestore(&phba->isr_lock, flags); - beiscsi_process_mcc_cq(phba); - } - - if (pbe_eq->todo_cq) { - spin_lock_irqsave(&phba->isr_lock, flags); - pbe_eq->todo_cq = false; - spin_unlock_irqrestore(&phba->isr_lock, flags); - beiscsi_process_cq(pbe_eq, BE2_MAX_NUM_CQ_PROC); - } - - /* rearm EQ for further interrupts */ - hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); -} - static int be_iopoll(struct irq_poll *iop, int budget) { - unsigned int ret, num_eq_processed; + unsigned int ret, io_events; struct beiscsi_hba *phba; struct be_eq_obj *pbe_eq; struct be_eq_entry *eqe = NULL; struct be_queue_info *eq; - num_eq_processed = 0; + io_events = 0; pbe_eq = container_of(iop, struct be_eq_obj, iopoll); phba = pbe_eq->phba; eq = &pbe_eq->q; @@ -2294,10 +2259,10 @@ static int be_iopoll(struct irq_poll *iop, int budget) AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); eqe = queue_tail_node(eq); - num_eq_processed++; + io_events++; } - hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1); + hwi_ring_eq_db(phba, eq->id, 1, io_events, 0, 1); ret = beiscsi_process_cq(pbe_eq, budget); pbe_eq->cq_count += ret; @@ -5578,7 +5543,7 @@ static void beiscsi_eeh_resume(struct pci_dev *pdev) i = (phba->msix_enabled) ? i : 0; /* Work item for MCC handling */ pbe_eq = &phwi_context->be_eq[i]; - INIT_WORK(&pbe_eq->work_cqs, beiscsi_process_all_cqs); + INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work); ret = beiscsi_init_irqs(phba); if (ret < 0) { @@ -5682,7 +5647,6 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, spin_lock_init(&phba->io_sgl_lock); spin_lock_init(&phba->mgmt_sgl_lock); - spin_lock_init(&phba->isr_lock); spin_lock_init(&phba->async_pdu_lock); ret = mgmt_get_fw_config(&phba->ctrl, phba); if (ret != 0) { @@ -5754,7 +5718,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, i = (phba->msix_enabled) ? i : 0; /* Work item for MCC handling */ pbe_eq = &phwi_context->be_eq[i]; - INIT_WORK(&pbe_eq->work_cqs, beiscsi_process_all_cqs); + INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work); ret = beiscsi_init_irqs(phba); if (ret < 0) { diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 30a4606d9a3b..90cdc10c01fb 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -378,7 +378,6 @@ struct beiscsi_hba { struct sgl_handle **eh_sgl_hndl_base; spinlock_t io_sgl_lock; spinlock_t mgmt_sgl_lock; - spinlock_t isr_lock; spinlock_t async_pdu_lock; unsigned int age; struct list_head hba_queue; @@ -845,7 +844,6 @@ struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, void free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle); -void beiscsi_process_all_cqs(struct work_struct *work); void beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn, struct iscsi_task *task); From 9122e991cebb90a7225109ed7627950f485c5f58 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:11 +0530 Subject: [PATCH 042/173] scsi: be2iscsi: Fix checks for HBA in error state Save ue_detected and fw_timeout errors in state field of beiscsi_hba. BEISCSI_HBA_RUNNING BEISCSI_HBA_LINK_UP BEISCSI_HBA_BOOT_FOUND BEISCSI_HBA_PCI_ERR BEISCSI_HBA_FW_TIMEOUT BEISCSI_HBA_IN_UE Make sure no PCI transaction happens once in error state. Add checks in IO path to detect HBA in error. Skip hwi_purge_eq step which can't be done in error state. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 28 +++++---- drivers/scsi/be2iscsi/be_iscsi.c | 81 +++++++++++--------------- drivers/scsi/be2iscsi/be_main.c | 97 ++++++++++++++++++++++---------- drivers/scsi/be2iscsi/be_main.h | 36 ++++++------ drivers/scsi/be2iscsi/be_mgmt.c | 5 +- 5 files changed, 134 insertions(+), 113 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 403296239ae9..f16de6cf2678 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -152,8 +152,11 @@ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, struct be_cmd_resp_hdr *mbx_resp_hdr; struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; - if (beiscsi_error(phba)) - return -EPERM; + if (beiscsi_hba_in_error(phba)) { + clear_bit(MCC_TAG_STATE_RUNNING, + &phba->ctrl.ptag_state[tag].tag_state); + return -EIO; + } /* wait for the mccq completion */ rc = wait_event_interruptible_timeout( @@ -315,13 +318,16 @@ static void beiscsi_process_async_link(struct beiscsi_hba *phba, * This has been newly introduced in SKH-R Firmware 10.0.338.45. **/ if (evt->port_link_status & BE_ASYNC_LINK_UP_MASK) { - phba->state = BE_ADAPTER_LINK_UP | BE_ADAPTER_CHECK_BOOT; phba->get_boot = BE_GET_BOOT_RETRIES; + /* first this needs to be visible to worker thread */ + wmb(); + set_bit(BEISCSI_HBA_LINK_UP | BEISCSI_HBA_BOOT_FOUND, + &phba->state); __beiscsi_log(phba, KERN_ERR, "BC_%d : Link Up on Port %d tag 0x%x\n", evt->physical_port, evt->event_tag); } else { - phba->state = BE_ADAPTER_LINK_DOWN; + clear_bit(BEISCSI_HBA_LINK_UP, &phba->state); __beiscsi_log(phba, KERN_ERR, "BC_%d : Link Down on Port %d tag 0x%x\n", evt->physical_port, evt->event_tag); @@ -406,8 +412,10 @@ void beiscsi_process_async_event(struct beiscsi_hba *phba, beiscsi_process_async_link(phba, compl); break; case ASYNC_EVENT_CODE_ISCSI: - phba->state |= BE_ADAPTER_CHECK_BOOT; phba->get_boot = BE_GET_BOOT_RETRIES; + /* first this needs to be visible to worker thread */ + wmb(); + set_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state); sev = KERN_ERR; break; case ASYNC_EVENT_CODE_SLI: @@ -504,7 +512,7 @@ int be_mcc_compl_poll(struct beiscsi_hba *phba, unsigned int tag) return 0; } for (i = 0; i < mcc_timeout; i++) { - if (beiscsi_error(phba)) + if (beiscsi_hba_in_error(phba)) return -EIO; beiscsi_process_mcc_cq(phba); @@ -522,7 +530,7 @@ int be_mcc_compl_poll(struct beiscsi_hba *phba, unsigned int tag) beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, "BC_%d : FW Timed Out\n"); - phba->fw_timeout = true; + set_bit(BEISCSI_HBA_FW_TIMEOUT, &phba->state); beiscsi_ue_detect(phba); return -EBUSY; } @@ -566,7 +574,7 @@ static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl) */ timeout = jiffies + msecs_to_jiffies(BEISCSI_MBX_RDY_BIT_TIMEOUT); do { - if (beiscsi_error(phba)) + if (beiscsi_hba_in_error(phba)) return -EIO; ready = ioread32(db); @@ -586,10 +594,8 @@ static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl) beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, "BC_%d : FW Timed Out\n"); - - phba->fw_timeout = true; + set_bit(BEISCSI_HBA_FW_TIMEOUT, &phba->state); beiscsi_ue_detect(phba); - return -EBUSY; } diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index faa37f67a8e6..ddb458abbf59 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -52,22 +52,20 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, if (!ep) { - printk(KERN_ERR - "beiscsi_session_create: invalid ep\n"); + pr_err("beiscsi_session_create: invalid ep\n"); return NULL; } beiscsi_ep = ep->dd_data; phba = beiscsi_ep->phba; - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : PCI_ERROR Recovery\n"); - return NULL; - } else { + if (beiscsi_hba_in_error(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, - "BS_%d : In beiscsi_session_create\n"); + "BS_%d : HBA in error 0x%lx\n", phba->state); + return NULL; } + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : In beiscsi_session_create\n"); if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : Cannot handle %d cmds." @@ -436,9 +434,9 @@ int beiscsi_iface_set_param(struct Scsi_Host *shost, uint32_t rm_len = dt_len; int ret; - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); + if (beiscsi_hba_in_error(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; } @@ -579,9 +577,9 @@ int beiscsi_iface_get_param(struct iscsi_iface *iface, if (param_type != ISCSI_NET_PARAM) return 0; - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); + if (beiscsi_hba_in_error(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; } @@ -737,7 +735,7 @@ static void beiscsi_get_port_state(struct Scsi_Host *shost) struct beiscsi_hba *phba = iscsi_host_priv(shost); struct iscsi_cls_host *ihost = shost->shost_data; - ihost->port_state = (phba->state & BE_ADAPTER_LINK_UP) ? + ihost->port_state = test_bit(BEISCSI_HBA_LINK_UP, &phba->state) ? ISCSI_PORT_STATE_UP : ISCSI_PORT_STATE_DOWN; } @@ -789,16 +787,13 @@ int beiscsi_get_host_param(struct Scsi_Host *shost, struct beiscsi_hba *phba = iscsi_host_priv(shost); int status = 0; - - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); - return -EBUSY; - } else { + if (beiscsi_hba_in_error(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, - "BS_%d : In beiscsi_get_host_param," - " param = %d\n", param); + "BS_%d : HBA in error 0x%lx\n", phba->state); + return -EBUSY; } + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : In beiscsi_get_host_param, param = %d\n", param); switch (param) { case ISCSI_HOST_PARAM_HWADDRESS: @@ -940,15 +935,13 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn) phba = ((struct beiscsi_conn *)conn->dd_data)->phba; - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); + if (beiscsi_hba_in_error(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; - } else { - beiscsi_log(beiscsi_conn->phba, KERN_INFO, - BEISCSI_LOG_CONFIG, - "BS_%d : In beiscsi_conn_start\n"); } + beiscsi_log(beiscsi_conn->phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : In beiscsi_conn_start\n"); memset(¶ms, 0, sizeof(struct beiscsi_offload_params)); beiscsi_ep = beiscsi_conn->ep; @@ -1165,28 +1158,20 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, struct iscsi_endpoint *ep; int ret; - if (shost) - phba = iscsi_host_priv(shost); - else { + if (!shost) { ret = -ENXIO; - printk(KERN_ERR - "beiscsi_ep_connect shost is NULL\n"); + pr_err("beiscsi_ep_connect shost is NULL\n"); return ERR_PTR(ret); } - if (beiscsi_error(phba)) { + phba = iscsi_host_priv(shost); + if (beiscsi_hba_in_error(phba)) { ret = -EIO; - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BS_%d : The FW state Not Stable!!!\n"); + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); return ERR_PTR(ret); } - - if (phba->state & BE_ADAPTER_PCI_ERR) { - ret = -EBUSY; - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); - return ERR_PTR(ret); - } else if (phba->state & BE_ADAPTER_LINK_DOWN) { + if (!test_bit(BEISCSI_HBA_LINK_UP, &phba->state)) { ret = -EBUSY; beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, "BS_%d : The Adapter Port state is Down!!!\n"); @@ -1340,9 +1325,9 @@ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep) tcp_upload_flag = CONNECTION_UPLOAD_ABORT; } - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : PCI_ERROR Recovery\n"); + if (beiscsi_hba_in_error(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); goto free_ep; } diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 6179c4e512a6..22fcbea6894d 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -2017,6 +2017,9 @@ void beiscsi_process_mcc_cq(struct beiscsi_hba *phba) mcc_compl = queue_tail_node(mcc_cq); mcc_compl->flags = le32_to_cpu(mcc_compl->flags); while (mcc_compl->flags & CQE_FLAGS_VALID_MASK) { + if (beiscsi_hba_in_error(phba)) + return; + if (num_processed >= 32) { hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 0); @@ -2048,7 +2051,8 @@ static void beiscsi_mcc_work(struct work_struct *work) phba = pbe_eq->phba; beiscsi_process_mcc_cq(phba); /* rearm EQ for further interrupts */ - hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); + if (!beiscsi_hba_in_error(phba)) + hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); } /** @@ -2079,6 +2083,9 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] & CQE_VALID_MASK) { + if (beiscsi_hba_in_error(phba)) + return 0; + be_dws_le_to_cpu(sol, sizeof(struct sol_cqe)); code = (sol->dw[offsetof(struct amap_sol_cqe, code) / @@ -2248,12 +2255,16 @@ static int be_iopoll(struct irq_poll *iop, int budget) struct be_eq_entry *eqe = NULL; struct be_queue_info *eq; - io_events = 0; pbe_eq = container_of(iop, struct be_eq_obj, iopoll); phba = pbe_eq->phba; + if (beiscsi_hba_in_error(phba)) { + irq_poll_complete(iop); + return 0; + } + + io_events = 0; eq = &pbe_eq->q; eqe = queue_tail_node(eq); - while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); @@ -2261,7 +2272,6 @@ static int be_iopoll(struct irq_poll *iop, int budget) eqe = queue_tail_node(eq); io_events++; } - hwi_ring_eq_db(phba, eq->id, 1, io_events, 0, 1); ret = beiscsi_process_cq(pbe_eq, budget); @@ -2272,7 +2282,8 @@ static int be_iopoll(struct irq_poll *iop, int budget) BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO, "BM_%d : rearm pbe_eq->q.id =%d ret %d\n", pbe_eq->q.id, ret); - hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); + if (!beiscsi_hba_in_error(phba)) + hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); } return ret; } @@ -4633,7 +4644,8 @@ static void beiscsi_cleanup_task(struct iscsi_task *task) } if (io_task->scsi_cmnd) { - scsi_dma_unmap(io_task->scsi_cmnd); + if (io_task->num_sg) + scsi_dma_unmap(io_task->scsi_cmnd); io_task->scsi_cmnd = NULL; } } else { @@ -5112,6 +5124,15 @@ static int beiscsi_task_xmit(struct iscsi_task *task) int num_sg; unsigned int writedir = 0, xferlen = 0; + phba = io_task->conn->phba; + /** + * HBA in error includes BEISCSI_HBA_FW_TIMEOUT. IO path might be + * operational if FW still gets heartbeat from EP FW. Is management + * path really needed to continue further? + */ + if (beiscsi_hba_in_error(phba)) + return -EIO; + if (!io_task->conn->login_in_progress) task->hdr->exp_statsn = 0; @@ -5119,8 +5140,8 @@ static int beiscsi_task_xmit(struct iscsi_task *task) return beiscsi_mtask(task); io_task->scsi_cmnd = sc; + io_task->num_sg = 0; num_sg = scsi_dma_map(sc); - phba = io_task->conn->phba; if (num_sg < 0) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_IO | BEISCSI_LOG_ISCSI, @@ -5131,6 +5152,11 @@ static int beiscsi_task_xmit(struct iscsi_task *task) return num_sg; } + /** + * For scsi cmd task, check num_sg before unmapping in cleanup_task. + * For management task, cleanup_task checks mtask_addr before unmapping. + */ + io_task->num_sg = num_sg; xferlen = scsi_bufflen(sc); sg = scsi_sglist(sc); if (sc->sc_data_direction == DMA_TO_DEVICE) @@ -5160,6 +5186,12 @@ static int beiscsi_bsg_request(struct bsg_job *job) shost = iscsi_job_to_shost(job); phba = iscsi_host_priv(shost); + if (beiscsi_hba_in_error(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BM_%d : HBA in error 0x%lx\n", phba->state); + return -ENXIO; + } + switch (bsg_req->msgcode) { case ISCSI_BSG_HST_VENDOR: nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev, @@ -5233,12 +5265,10 @@ void beiscsi_hba_attrs_init(struct beiscsi_hba *phba) /* * beiscsi_quiesce()- Cleanup Driver resources * @phba: Instance Priv structure - * @unload_state:i Clean or EEH unload state * * Free the OS and HW resources held by the driver **/ -static void beiscsi_quiesce(struct beiscsi_hba *phba, - uint32_t unload_state) +static void beiscsi_quiesce(struct beiscsi_hba *phba) { struct hwi_controller *phwi_ctrlr; struct hwi_context_memory *phwi_context; @@ -5265,21 +5295,22 @@ static void beiscsi_quiesce(struct beiscsi_hba *phba, irq_poll_disable(&pbe_eq->iopoll); } - if (unload_state == BEISCSI_CLEAN_UNLOAD) { - destroy_workqueue(phba->wq); - beiscsi_clean_port(phba); - beiscsi_free_mem(phba); - - beiscsi_unmap_pci_function(phba); - pci_free_consistent(phba->pcidev, - phba->ctrl.mbox_mem_alloced.size, - phba->ctrl.mbox_mem_alloced.va, - phba->ctrl.mbox_mem_alloced.dma); - } else { - hwi_purge_eq(phba); + /* PCI_ERR is set then check if driver is not unloading */ + if (test_bit(BEISCSI_HBA_RUNNING, &phba->state) && + test_bit(BEISCSI_HBA_PCI_ERR, &phba->state)) { hwi_cleanup(phba); + return; } + destroy_workqueue(phba->wq); + beiscsi_clean_port(phba); + beiscsi_free_mem(phba); + + beiscsi_unmap_pci_function(phba); + pci_free_consistent(phba->pcidev, + phba->ctrl.mbox_mem_alloced.size, + phba->ctrl.mbox_mem_alloced.va, + phba->ctrl.mbox_mem_alloced.dma); } static void beiscsi_remove(struct pci_dev *pcidev) @@ -5292,10 +5323,11 @@ static void beiscsi_remove(struct pci_dev *pcidev) return; } + clear_bit(BEISCSI_HBA_RUNNING, &phba->state); beiscsi_iface_destroy_default(phba); iscsi_boot_destroy_kset(phba->boot_kset); iscsi_host_remove(phba->shost); - beiscsi_quiesce(phba, BEISCSI_CLEAN_UNLOAD); + beiscsi_quiesce(phba); pci_dev_put(phba->pcidev); iscsi_host_free(phba->shost); pci_disable_pcie_error_reporting(pcidev); @@ -5331,6 +5363,9 @@ static void be_eqd_update(struct beiscsi_hba *phba) u32 pps, delta; unsigned int tag; + if (beiscsi_hba_in_error(phba)) + return; + phwi_ctrlr = phba->phwi_ctrlr; phwi_context = phwi_ctrlr->phwi_ctxt; @@ -5372,6 +5407,9 @@ static void be_eqd_update(struct beiscsi_hba *phba) static void be_check_boot_session(struct beiscsi_hba *phba) { + if (beiscsi_hba_in_error(phba)) + return; + if (beiscsi_setup_boot_info(phba)) beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Could not set up " @@ -5393,13 +5431,13 @@ beiscsi_hw_health_check(struct work_struct *work) be_eqd_update(phba); - if (phba->state & BE_ADAPTER_CHECK_BOOT) { + if (test_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state)) { if ((phba->get_boot > 0) && (!phba->boot_kset)) { phba->get_boot--; if (!(phba->get_boot % BE_GET_BOOT_TO)) be_check_boot_session(phba); } else { - phba->state &= ~BE_ADAPTER_CHECK_BOOT; + clear_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state); phba->get_boot = 0; } } @@ -5417,12 +5455,12 @@ static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev, struct beiscsi_hba *phba = NULL; phba = (struct beiscsi_hba *)pci_get_drvdata(pdev); - phba->state |= BE_ADAPTER_PCI_ERR; + set_bit(BEISCSI_HBA_PCI_ERR, &phba->state); beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : EEH error detected\n"); - beiscsi_quiesce(phba, BEISCSI_EEH_UNLOAD); + beiscsi_quiesce(phba); if (state == pci_channel_io_perm_failure) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, @@ -5554,7 +5592,7 @@ static void beiscsi_eeh_resume(struct pci_dev *pdev) } hwi_enable_intr(phba); - phba->state &= ~BE_ADAPTER_PCI_ERR; + clear_bit(BEISCSI_HBA_PCI_ERR, &phba->state); return; ret_err: @@ -5597,10 +5635,8 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, /* Initialize Driver configuration Paramters */ beiscsi_hba_attrs_init(phba); - phba->fw_timeout = false; phba->mac_addr_set = false; - switch (pcidev->device) { case BE_DEVICE_ID1: case OC_DEVICE_ID1: @@ -5629,6 +5665,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, goto hba_free; } + set_bit(BEISCSI_HBA_RUNNING, &phba->state); /* * FUNCTION_RESET should clean up any stale info in FW for this fn */ diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 90cdc10c01fb..8ab16516ae9a 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -98,20 +98,9 @@ #define INVALID_SESS_HANDLE 0xFFFFFFFF -/** - * Adapter States - **/ -#define BE_ADAPTER_LINK_UP 0x001 -#define BE_ADAPTER_LINK_DOWN 0x002 -#define BE_ADAPTER_PCI_ERR 0x004 -#define BE_ADAPTER_CHECK_BOOT 0x008 - - -#define BEISCSI_CLEAN_UNLOAD 0x01 -#define BEISCSI_EEH_UNLOAD 0x02 - #define BE_GET_BOOT_RETRIES 45 #define BE_GET_BOOT_TO 20 + /** * hardware needs the async PDU buffers to be posted in multiples of 8 * So have atleast 8 of them by default @@ -417,11 +406,20 @@ struct beiscsi_hba { unsigned long ulp_supported; } fw_config; - unsigned int state; + unsigned long state; +#define BEISCSI_HBA_RUNNING 0 +#define BEISCSI_HBA_LINK_UP 1 +#define BEISCSI_HBA_BOOT_FOUND 2 +#define BEISCSI_HBA_PCI_ERR 3 +#define BEISCSI_HBA_FW_TIMEOUT 4 +#define BEISCSI_HBA_IN_UE 5 +/* error bits */ +#define BEISCSI_HBA_IN_ERR ((1 << BEISCSI_HBA_PCI_ERR) | \ + (1 << BEISCSI_HBA_FW_TIMEOUT) | \ + (1 << BEISCSI_HBA_IN_UE)) + u8 optic_state; int get_boot; - bool fw_timeout; - bool ue_detected; struct delayed_work beiscsi_hw_check_task; bool mac_addr_set; @@ -445,6 +443,8 @@ struct beiscsi_hba { uint32_t writedir); }; +#define beiscsi_hba_in_error(phba) ((phba)->state & BEISCSI_HBA_IN_ERR) + struct beiscsi_session { struct pci_pool *bhs_pool; }; @@ -507,6 +507,7 @@ struct beiscsi_io_task { struct sgl_handle *psgl_handle; struct beiscsi_conn *conn; struct scsi_cmnd *scsi_cmnd; + int num_sg; struct hwi_wrb_context *pwrb_context; unsigned int cmd_sn; unsigned int flags; @@ -854,11 +855,6 @@ void hwi_ring_cq_db(struct beiscsi_hba *phba, unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget); void beiscsi_process_mcc_cq(struct beiscsi_hba *phba); -static inline bool beiscsi_error(struct beiscsi_hba *phba) -{ - return phba->ue_detected || phba->fw_timeout; -} - struct pdu_nop_out { u32 dw[12]; }; diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 8069ef0ce533..4f2194e324ae 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -108,9 +108,6 @@ void beiscsi_ue_detect(struct beiscsi_hba *phba) uint32_t ue_mask_hi = 0, ue_mask_lo = 0; uint8_t i = 0; - if (phba->ue_detected) - return; - pci_read_config_dword(phba->pcidev, PCICFG_UE_STATUS_LOW, &ue_lo); pci_read_config_dword(phba->pcidev, @@ -128,7 +125,7 @@ void beiscsi_ue_detect(struct beiscsi_hba *phba) if (ue_lo || ue_hi) { - phba->ue_detected = true; + set_bit(BEISCSI_HBA_IN_UE, &phba->state); beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, "BG_%d : Error detected on the adapter\n"); From 50a4b824be9e4a01f3b87790865e26b1546fcbb8 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:12 +0530 Subject: [PATCH 043/173] scsi: be2iscsi: Fix to make boot discovery non-blocking Boot work involves: 1. Find and fetch configured boot session and its handle. 2. Attempt to open the session if its not. 3. Get the session details for boot kset creation. 4. Logout of that session owned by FW. 5. Create boot kset for session details. All these actions were done in blocking call with retries in global wq. Other works in wq suffered if the IOCTLs stalled or timed out. This change moves all the boot work to make it non-blocking. The work queued in global wq just issues the IOCTL depending on the action to be taken and mcc wq schedules work depending on status of the IOCTL. Initial boot_work is started on link and ASYNC event. The other code changes move all boot related functions in one place and follow naming conventions. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be.h | 2 + drivers/scsi/be2iscsi/be_cmds.c | 161 +++++---- drivers/scsi/be2iscsi/be_cmds.h | 18 +- drivers/scsi/be2iscsi/be_main.c | 608 +++++++++++++++----------------- drivers/scsi/be2iscsi/be_main.h | 33 +- drivers/scsi/be2iscsi/be_mgmt.c | 539 +++++++++++++++------------- drivers/scsi/be2iscsi/be_mgmt.h | 23 +- 7 files changed, 726 insertions(+), 658 deletions(-) diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h index 621291ab0214..454002d98bcd 100644 --- a/drivers/scsi/be2iscsi/be.h +++ b/drivers/scsi/be2iscsi/be.h @@ -113,6 +113,8 @@ struct beiscsi_mcc_tag_state { unsigned long tag_state; #define MCC_TAG_STATE_RUNNING 1 #define MCC_TAG_STATE_TIMEOUT 2 +#define MCC_TAG_STATE_ASYNC 3 + void (*cbfn)(struct beiscsi_hba *, unsigned int); struct be_dma_mem tag_mem_state; }; diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index f16de6cf2678..27d10cee0c40 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -84,6 +84,7 @@ struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index] = 0; phba->ctrl.mcc_tag_status[tag] = 0; phba->ctrl.ptag_state[tag].tag_state = 0; + phba->ctrl.ptag_state[tag].cbfn = NULL; phba->ctrl.mcc_tag_available--; if (phba->ctrl.mcc_alloc_index == (MAX_MCC_CMD - 1)) phba->ctrl.mcc_alloc_index = 0; @@ -127,6 +128,70 @@ void beiscsi_fail_session(struct iscsi_cls_session *cls_session) iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); } +/* + * beiscsi_mcc_compl_status - Return the status of MCC completion + * @phba: Driver private structure + * @tag: Tag for the MBX Command + * @wrb: the WRB used for the MBX Command + * @mbx_cmd_mem: ptr to memory allocated for MBX Cmd + * + * return + * Success: 0 + * Failure: Non-Zero + */ +int __beiscsi_mcc_compl_status(struct beiscsi_hba *phba, + unsigned int tag, + struct be_mcc_wrb **wrb, + struct be_dma_mem *mbx_cmd_mem) +{ + struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; + uint16_t status = 0, addl_status = 0, wrb_num = 0; + struct be_cmd_resp_hdr *mbx_resp_hdr; + struct be_cmd_req_hdr *mbx_hdr; + struct be_mcc_wrb *temp_wrb; + uint32_t mcc_tag_status; + int rc = 0; + + mcc_tag_status = phba->ctrl.mcc_tag_status[tag]; + status = (mcc_tag_status & CQE_STATUS_MASK); + addl_status = ((mcc_tag_status & CQE_STATUS_ADDL_MASK) >> + CQE_STATUS_ADDL_SHIFT); + + if (mbx_cmd_mem) { + mbx_hdr = (struct be_cmd_req_hdr *)mbx_cmd_mem->va; + } else { + wrb_num = (mcc_tag_status & CQE_STATUS_WRB_MASK) >> + CQE_STATUS_WRB_SHIFT; + temp_wrb = (struct be_mcc_wrb *)queue_get_wrb(mccq, wrb_num); + mbx_hdr = embedded_payload(temp_wrb); + + if (wrb) + *wrb = temp_wrb; + } + + if (status || addl_status) { + beiscsi_log(phba, KERN_WARNING, + BEISCSI_LOG_INIT | BEISCSI_LOG_EH | + BEISCSI_LOG_CONFIG, + "BC_%d : MBX Cmd Failed for Subsys : %d Opcode : %d with Status : %d and Extd_Status : %d\n", + mbx_hdr->subsystem, mbx_hdr->opcode, + status, addl_status); + rc = -EIO; + if (status == MCC_STATUS_INSUFFICIENT_BUFFER) { + mbx_resp_hdr = (struct be_cmd_resp_hdr *)mbx_hdr; + beiscsi_log(phba, KERN_WARNING, + BEISCSI_LOG_INIT | BEISCSI_LOG_EH | + BEISCSI_LOG_CONFIG, + "BC_%d : Insufficient Buffer Error Resp_Len : %d Actual_Resp_Len : %d\n", + mbx_resp_hdr->response_length, + mbx_resp_hdr->actual_resp_len); + rc = -EAGAIN; + } + } + + return rc; +} + /* * beiscsi_mccq_compl_wait()- Process completion in MCC CQ * @phba: Driver private structure @@ -141,16 +206,11 @@ void beiscsi_fail_session(struct iscsi_cls_session *cls_session) * Failure: Non-Zero **/ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, - uint32_t tag, struct be_mcc_wrb **wrb, + unsigned int tag, + struct be_mcc_wrb **wrb, struct be_dma_mem *mbx_cmd_mem) { int rc = 0; - uint32_t mcc_tag_status; - uint16_t status = 0, addl_status = 0, wrb_num = 0; - struct be_mcc_wrb *temp_wrb; - struct be_cmd_req_hdr *mbx_hdr; - struct be_cmd_resp_hdr *mbx_resp_hdr; - struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; if (beiscsi_hba_in_error(phba)) { clear_bit(MCC_TAG_STATE_RUNNING, @@ -159,11 +219,11 @@ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, } /* wait for the mccq completion */ - rc = wait_event_interruptible_timeout( - phba->ctrl.mcc_wait[tag], - phba->ctrl.mcc_tag_status[tag], - msecs_to_jiffies( - BEISCSI_HOST_MBX_TIMEOUT)); + rc = wait_event_interruptible_timeout(phba->ctrl.mcc_wait[tag], + phba->ctrl.mcc_tag_status[tag], + msecs_to_jiffies( + BEISCSI_HOST_MBX_TIMEOUT)); + /** * If MBOX cmd timeout expired, tag and resource allocated * for cmd is not freed until FW returns completion. @@ -197,47 +257,7 @@ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, return -EBUSY; } - rc = 0; - mcc_tag_status = phba->ctrl.mcc_tag_status[tag]; - status = (mcc_tag_status & CQE_STATUS_MASK); - addl_status = ((mcc_tag_status & CQE_STATUS_ADDL_MASK) >> - CQE_STATUS_ADDL_SHIFT); - - if (mbx_cmd_mem) { - mbx_hdr = (struct be_cmd_req_hdr *)mbx_cmd_mem->va; - } else { - wrb_num = (mcc_tag_status & CQE_STATUS_WRB_MASK) >> - CQE_STATUS_WRB_SHIFT; - temp_wrb = (struct be_mcc_wrb *)queue_get_wrb(mccq, wrb_num); - mbx_hdr = embedded_payload(temp_wrb); - - if (wrb) - *wrb = temp_wrb; - } - - if (status || addl_status) { - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_INIT | BEISCSI_LOG_EH | - BEISCSI_LOG_CONFIG, - "BC_%d : MBX Cmd Failed for " - "Subsys : %d Opcode : %d with " - "Status : %d and Extd_Status : %d\n", - mbx_hdr->subsystem, - mbx_hdr->opcode, - status, addl_status); - rc = -EIO; - if (status == MCC_STATUS_INSUFFICIENT_BUFFER) { - mbx_resp_hdr = (struct be_cmd_resp_hdr *) mbx_hdr; - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_INIT | BEISCSI_LOG_EH | - BEISCSI_LOG_CONFIG, - "BC_%d : Insufficient Buffer Error " - "Resp_Len : %d Actual_Resp_Len : %d\n", - mbx_resp_hdr->response_length, - mbx_resp_hdr->actual_resp_len); - rc = -EAGAIN; - } - } + rc = __beiscsi_mcc_compl_status(phba, tag, wrb, mbx_cmd_mem); free_mcc_wrb(&phba->ctrl, tag); return rc; @@ -318,11 +338,9 @@ static void beiscsi_process_async_link(struct beiscsi_hba *phba, * This has been newly introduced in SKH-R Firmware 10.0.338.45. **/ if (evt->port_link_status & BE_ASYNC_LINK_UP_MASK) { - phba->get_boot = BE_GET_BOOT_RETRIES; - /* first this needs to be visible to worker thread */ - wmb(); - set_bit(BEISCSI_HBA_LINK_UP | BEISCSI_HBA_BOOT_FOUND, - &phba->state); + set_bit(BEISCSI_HBA_LINK_UP, &phba->state); + if (test_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state)) + beiscsi_start_boot_work(phba, BE_BOOT_INVALID_SHANDLE); __beiscsi_log(phba, KERN_ERR, "BC_%d : Link Up on Port %d tag 0x%x\n", evt->physical_port, evt->event_tag); @@ -412,10 +430,8 @@ void beiscsi_process_async_event(struct beiscsi_hba *phba, beiscsi_process_async_link(phba, compl); break; case ASYNC_EVENT_CODE_ISCSI: - phba->get_boot = BE_GET_BOOT_RETRIES; - /* first this needs to be visible to worker thread */ - wmb(); - set_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state); + if (test_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state)) + beiscsi_start_boot_work(phba, BE_BOOT_INVALID_SHANDLE); sev = KERN_ERR; break; case ASYNC_EVENT_CODE_SLI: @@ -451,6 +467,9 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, return 0; } + /* end MCC with this tag */ + clear_bit(MCC_TAG_STATE_RUNNING, &ctrl->ptag_state[tag].tag_state); + if (test_bit(MCC_TAG_STATE_TIMEOUT, &ctrl->ptag_state[tag].tag_state)) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_MBOX | BEISCSI_LOG_INIT | @@ -461,9 +480,11 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, * Only for non-embedded cmd, PCI resource is allocated. **/ tag_mem = &ctrl->ptag_state[tag].tag_mem_state; - if (tag_mem->size) + if (tag_mem->size) { pci_free_consistent(ctrl->pdev, tag_mem->size, tag_mem->va, tag_mem->dma); + tag_mem->size = 0; + } free_mcc_wrb(ctrl, tag); return 0; } @@ -482,8 +503,18 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, CQE_STATUS_ADDL_MASK; ctrl->mcc_tag_status[tag] |= (compl_status & CQE_STATUS_MASK); - /* write ordering forced in wake_up_interruptible */ - clear_bit(MCC_TAG_STATE_RUNNING, &ctrl->ptag_state[tag].tag_state); + if (test_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state)) { + if (ctrl->ptag_state[tag].cbfn) + ctrl->ptag_state[tag].cbfn(phba, tag); + else + beiscsi_log(phba, KERN_ERR, + BEISCSI_LOG_MBOX | BEISCSI_LOG_INIT | + BEISCSI_LOG_CONFIG, + "BC_%d : MBX ASYNC command with no callback\n"); + free_mcc_wrb(ctrl, tag); + return 0; + } + wake_up_interruptible(&ctrl->mcc_wait[tag]); return 0; } diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index c50b74afd00a..0510b6767341 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -345,8 +345,8 @@ struct be_cmd_req_logout_fw_sess { struct be_cmd_resp_logout_fw_sess { struct be_cmd_resp_hdr hdr; /* dw[4] */ -#define BEISCSI_MGMT_SESSION_CLOSE 0x20 uint32_t session_status; +#define BE_SESS_STATUS_CLOSE 0x20 } __packed; struct mgmt_conn_login_options { @@ -438,8 +438,13 @@ struct be_cmd_get_boot_target_req { struct be_cmd_get_boot_target_resp { struct be_cmd_resp_hdr hdr; - u32 boot_session_count; - int boot_session_handle; + u32 boot_session_count; + u32 boot_session_handle; +/** + * FW returns 0xffffffff if it couldn't establish connection with + * configured boot target. + */ +#define BE_BOOT_INVALID_SHANDLE 0xffffffff }; struct be_cmd_reopen_session_req { @@ -741,8 +746,13 @@ void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag); int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *, int num); int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, - uint32_t tag, struct be_mcc_wrb **wrb, + unsigned int tag, + struct be_mcc_wrb **wrb, struct be_dma_mem *mbx_cmd_mem); +int __beiscsi_mcc_compl_status(struct beiscsi_hba *phba, + unsigned int tag, + struct be_mcc_wrb **wrb, + struct be_dma_mem *mbx_cmd_mem); /*ISCSI Functuions */ int be_cmd_fw_initialize(struct be_ctrl_info *ctrl); int be_cmd_fw_uninit(struct be_ctrl_info *ctrl); diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 22fcbea6894d..eb4ce17a683d 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -374,152 +374,6 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc) return iscsi_eh_device_reset(sc); } -static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf) -{ - struct beiscsi_hba *phba = data; - struct mgmt_session_info *boot_sess = &phba->boot_sess; - struct mgmt_conn_info *boot_conn = &boot_sess->conn_list[0]; - char *str = buf; - int rc = -EPERM; - - switch (type) { - case ISCSI_BOOT_TGT_NAME: - rc = sprintf(buf, "%.*s\n", - (int)strlen(boot_sess->target_name), - (char *)&boot_sess->target_name); - break; - case ISCSI_BOOT_TGT_IP_ADDR: - if (boot_conn->dest_ipaddr.ip_type == BEISCSI_IP_TYPE_V4) - rc = sprintf(buf, "%pI4\n", - (char *)&boot_conn->dest_ipaddr.addr); - else - rc = sprintf(str, "%pI6\n", - (char *)&boot_conn->dest_ipaddr.addr); - break; - case ISCSI_BOOT_TGT_PORT: - rc = sprintf(str, "%d\n", boot_conn->dest_port); - break; - - case ISCSI_BOOT_TGT_CHAP_NAME: - rc = sprintf(str, "%.*s\n", - boot_conn->negotiated_login_options.auth_data.chap. - target_chap_name_length, - (char *)&boot_conn->negotiated_login_options. - auth_data.chap.target_chap_name); - break; - case ISCSI_BOOT_TGT_CHAP_SECRET: - rc = sprintf(str, "%.*s\n", - boot_conn->negotiated_login_options.auth_data.chap. - target_secret_length, - (char *)&boot_conn->negotiated_login_options. - auth_data.chap.target_secret); - break; - case ISCSI_BOOT_TGT_REV_CHAP_NAME: - rc = sprintf(str, "%.*s\n", - boot_conn->negotiated_login_options.auth_data.chap. - intr_chap_name_length, - (char *)&boot_conn->negotiated_login_options. - auth_data.chap.intr_chap_name); - break; - case ISCSI_BOOT_TGT_REV_CHAP_SECRET: - rc = sprintf(str, "%.*s\n", - boot_conn->negotiated_login_options.auth_data.chap. - intr_secret_length, - (char *)&boot_conn->negotiated_login_options. - auth_data.chap.intr_secret); - break; - case ISCSI_BOOT_TGT_FLAGS: - rc = sprintf(str, "2\n"); - break; - case ISCSI_BOOT_TGT_NIC_ASSOC: - rc = sprintf(str, "0\n"); - break; - } - return rc; -} - -static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf) -{ - struct beiscsi_hba *phba = data; - char *str = buf; - int rc = -EPERM; - - switch (type) { - case ISCSI_BOOT_INI_INITIATOR_NAME: - rc = sprintf(str, "%s\n", phba->boot_sess.initiator_iscsiname); - break; - } - return rc; -} - -static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf) -{ - struct beiscsi_hba *phba = data; - char *str = buf; - int rc = -EPERM; - - switch (type) { - case ISCSI_BOOT_ETH_FLAGS: - rc = sprintf(str, "2\n"); - break; - case ISCSI_BOOT_ETH_INDEX: - rc = sprintf(str, "0\n"); - break; - case ISCSI_BOOT_ETH_MAC: - rc = beiscsi_get_macaddr(str, phba); - break; - } - return rc; -} - - -static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type) -{ - umode_t rc = 0; - - switch (type) { - case ISCSI_BOOT_TGT_NAME: - case ISCSI_BOOT_TGT_IP_ADDR: - case ISCSI_BOOT_TGT_PORT: - case ISCSI_BOOT_TGT_CHAP_NAME: - case ISCSI_BOOT_TGT_CHAP_SECRET: - case ISCSI_BOOT_TGT_REV_CHAP_NAME: - case ISCSI_BOOT_TGT_REV_CHAP_SECRET: - case ISCSI_BOOT_TGT_NIC_ASSOC: - case ISCSI_BOOT_TGT_FLAGS: - rc = S_IRUGO; - break; - } - return rc; -} - -static umode_t beiscsi_ini_get_attr_visibility(void *data, int type) -{ - umode_t rc = 0; - - switch (type) { - case ISCSI_BOOT_INI_INITIATOR_NAME: - rc = S_IRUGO; - break; - } - return rc; -} - - -static umode_t beiscsi_eth_get_attr_visibility(void *data, int type) -{ - umode_t rc = 0; - - switch (type) { - case ISCSI_BOOT_ETH_FLAGS: - case ISCSI_BOOT_ETH_MAC: - case ISCSI_BOOT_ETH_INDEX: - rc = S_IRUGO; - break; - } - return rc; -} - /*------------------- PCI Driver operations and data ----------------- */ static const struct pci_device_id beiscsi_pci_id_table[] = { { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, @@ -4312,149 +4166,6 @@ static void hwi_disable_intr(struct beiscsi_hba *phba) "BM_%d : In hwi_disable_intr, Already Disabled\n"); } -/** - * beiscsi_get_boot_info()- Get the boot session info - * @phba: The device priv structure instance - * - * Get the boot target info and store in driver priv structure - * - * return values - * Success: 0 - * Failure: Non-Zero Value - **/ -static int beiscsi_get_boot_info(struct beiscsi_hba *phba) -{ - struct be_cmd_get_session_resp *session_resp; - struct be_dma_mem nonemb_cmd; - unsigned int tag; - unsigned int s_handle; - int ret = -ENOMEM; - - /* Get the session handle of the boot target */ - ret = be_mgmt_get_boot_shandle(phba, &s_handle); - if (ret) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BM_%d : No boot session\n"); - - if (ret == -ENXIO) - phba->get_boot = 0; - - - return ret; - } - phba->get_boot = 0; - nonemb_cmd.va = pci_zalloc_consistent(phba->ctrl.pdev, - sizeof(*session_resp), - &nonemb_cmd.dma); - if (nonemb_cmd.va == NULL) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BM_%d : Failed to allocate memory for" - "beiscsi_get_session_info\n"); - - return -ENOMEM; - } - - tag = mgmt_get_session_info(phba, s_handle, - &nonemb_cmd); - if (!tag) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BM_%d : beiscsi_get_session_info" - " Failed\n"); - - goto boot_freemem; - } - - ret = beiscsi_mccq_compl_wait(phba, tag, NULL, &nonemb_cmd); - if (ret) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BM_%d : beiscsi_get_session_info Failed"); - - if (ret != -EBUSY) - goto boot_freemem; - else - return ret; - } - - session_resp = nonemb_cmd.va ; - - memcpy(&phba->boot_sess, &session_resp->session_info, - sizeof(struct mgmt_session_info)); - - beiscsi_logout_fw_sess(phba, - phba->boot_sess.session_handle); - ret = 0; - -boot_freemem: - pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, - nonemb_cmd.va, nonemb_cmd.dma); - return ret; -} - -static void beiscsi_boot_release(void *data) -{ - struct beiscsi_hba *phba = data; - - scsi_host_put(phba->shost); -} - -static int beiscsi_setup_boot_info(struct beiscsi_hba *phba) -{ - struct iscsi_boot_kobj *boot_kobj; - - /* it has been created previously */ - if (phba->boot_kset) - return 0; - - /* get boot info using mgmt cmd */ - if (beiscsi_get_boot_info(phba)) - /* Try to see if we can carry on without this */ - return 0; - - phba->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no); - if (!phba->boot_kset) - return -ENOMEM; - - /* get a ref because the show function will ref the phba */ - if (!scsi_host_get(phba->shost)) - goto free_kset; - boot_kobj = iscsi_boot_create_target(phba->boot_kset, 0, phba, - beiscsi_show_boot_tgt_info, - beiscsi_tgt_get_attr_visibility, - beiscsi_boot_release); - if (!boot_kobj) - goto put_shost; - - if (!scsi_host_get(phba->shost)) - goto free_kset; - boot_kobj = iscsi_boot_create_initiator(phba->boot_kset, 0, phba, - beiscsi_show_boot_ini_info, - beiscsi_ini_get_attr_visibility, - beiscsi_boot_release); - if (!boot_kobj) - goto put_shost; - - if (!scsi_host_get(phba->shost)) - goto free_kset; - boot_kobj = iscsi_boot_create_ethernet(phba->boot_kset, 0, phba, - beiscsi_show_boot_eth_info, - beiscsi_eth_get_attr_visibility, - beiscsi_boot_release); - if (!boot_kobj) - goto put_shost; - return 0; - -put_shost: - scsi_host_put(phba->shost); -free_kset: - iscsi_boot_destroy_kset(phba->boot_kset); - phba->boot_kset = NULL; - return -ENOMEM; -} - static int beiscsi_init_port(struct beiscsi_hba *phba) { int ret; @@ -5289,6 +5000,7 @@ static void beiscsi_quiesce(struct beiscsi_hba *phba) free_irq(phba->pcidev->irq, phba); pci_disable_msix(phba->pcidev); cancel_delayed_work_sync(&phba->beiscsi_hw_check_task); + cancel_work_sync(&phba->boot_work); for (i = 0; i < phba->num_cpus; i++) { pbe_eq = &phwi_context->be_eq[i]; @@ -5325,9 +5037,10 @@ static void beiscsi_remove(struct pci_dev *pcidev) clear_bit(BEISCSI_HBA_RUNNING, &phba->state); beiscsi_iface_destroy_default(phba); - iscsi_boot_destroy_kset(phba->boot_kset); iscsi_host_remove(phba->shost); beiscsi_quiesce(phba); + /* after cancelling boot_work */ + iscsi_boot_destroy_kset(phba->boot_struct.boot_kset); pci_dev_put(phba->pcidev); iscsi_host_free(phba->shost); pci_disable_pcie_error_reporting(pcidev); @@ -5351,6 +5064,281 @@ static void beiscsi_msix_enable(struct beiscsi_hba *phba) return; } +void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle) +{ + if (phba->boot_struct.boot_kset) + return; + + /* skip if boot work is already in progress */ + if (test_and_set_bit(BEISCSI_HBA_BOOT_WORK, &phba->state)) + return; + + phba->boot_struct.retry = 3; + phba->boot_struct.tag = 0; + phba->boot_struct.s_handle = s_handle; + phba->boot_struct.action = BEISCSI_BOOT_GET_SHANDLE; + schedule_work(&phba->boot_work); +} + +static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf) +{ + struct beiscsi_hba *phba = data; + struct mgmt_session_info *boot_sess = &phba->boot_struct.boot_sess; + struct mgmt_conn_info *boot_conn = &boot_sess->conn_list[0]; + char *str = buf; + int rc = -EPERM; + + switch (type) { + case ISCSI_BOOT_TGT_NAME: + rc = sprintf(buf, "%.*s\n", + (int)strlen(boot_sess->target_name), + (char *)&boot_sess->target_name); + break; + case ISCSI_BOOT_TGT_IP_ADDR: + if (boot_conn->dest_ipaddr.ip_type == BEISCSI_IP_TYPE_V4) + rc = sprintf(buf, "%pI4\n", + (char *)&boot_conn->dest_ipaddr.addr); + else + rc = sprintf(str, "%pI6\n", + (char *)&boot_conn->dest_ipaddr.addr); + break; + case ISCSI_BOOT_TGT_PORT: + rc = sprintf(str, "%d\n", boot_conn->dest_port); + break; + + case ISCSI_BOOT_TGT_CHAP_NAME: + rc = sprintf(str, "%.*s\n", + boot_conn->negotiated_login_options.auth_data.chap. + target_chap_name_length, + (char *)&boot_conn->negotiated_login_options. + auth_data.chap.target_chap_name); + break; + case ISCSI_BOOT_TGT_CHAP_SECRET: + rc = sprintf(str, "%.*s\n", + boot_conn->negotiated_login_options.auth_data.chap. + target_secret_length, + (char *)&boot_conn->negotiated_login_options. + auth_data.chap.target_secret); + break; + case ISCSI_BOOT_TGT_REV_CHAP_NAME: + rc = sprintf(str, "%.*s\n", + boot_conn->negotiated_login_options.auth_data.chap. + intr_chap_name_length, + (char *)&boot_conn->negotiated_login_options. + auth_data.chap.intr_chap_name); + break; + case ISCSI_BOOT_TGT_REV_CHAP_SECRET: + rc = sprintf(str, "%.*s\n", + boot_conn->negotiated_login_options.auth_data.chap. + intr_secret_length, + (char *)&boot_conn->negotiated_login_options. + auth_data.chap.intr_secret); + break; + case ISCSI_BOOT_TGT_FLAGS: + rc = sprintf(str, "2\n"); + break; + case ISCSI_BOOT_TGT_NIC_ASSOC: + rc = sprintf(str, "0\n"); + break; + } + return rc; +} + +static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf) +{ + struct beiscsi_hba *phba = data; + char *str = buf; + int rc = -EPERM; + + switch (type) { + case ISCSI_BOOT_INI_INITIATOR_NAME: + rc = sprintf(str, "%s\n", + phba->boot_struct.boot_sess.initiator_iscsiname); + break; + } + return rc; +} + +static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf) +{ + struct beiscsi_hba *phba = data; + char *str = buf; + int rc = -EPERM; + + switch (type) { + case ISCSI_BOOT_ETH_FLAGS: + rc = sprintf(str, "2\n"); + break; + case ISCSI_BOOT_ETH_INDEX: + rc = sprintf(str, "0\n"); + break; + case ISCSI_BOOT_ETH_MAC: + rc = beiscsi_get_macaddr(str, phba); + break; + } + return rc; +} + + +static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type) +{ + umode_t rc = 0; + + switch (type) { + case ISCSI_BOOT_TGT_NAME: + case ISCSI_BOOT_TGT_IP_ADDR: + case ISCSI_BOOT_TGT_PORT: + case ISCSI_BOOT_TGT_CHAP_NAME: + case ISCSI_BOOT_TGT_CHAP_SECRET: + case ISCSI_BOOT_TGT_REV_CHAP_NAME: + case ISCSI_BOOT_TGT_REV_CHAP_SECRET: + case ISCSI_BOOT_TGT_NIC_ASSOC: + case ISCSI_BOOT_TGT_FLAGS: + rc = S_IRUGO; + break; + } + return rc; +} + +static umode_t beiscsi_ini_get_attr_visibility(void *data, int type) +{ + umode_t rc = 0; + + switch (type) { + case ISCSI_BOOT_INI_INITIATOR_NAME: + rc = S_IRUGO; + break; + } + return rc; +} + + +static umode_t beiscsi_eth_get_attr_visibility(void *data, int type) +{ + umode_t rc = 0; + + switch (type) { + case ISCSI_BOOT_ETH_FLAGS: + case ISCSI_BOOT_ETH_MAC: + case ISCSI_BOOT_ETH_INDEX: + rc = S_IRUGO; + break; + } + return rc; +} + +static void beiscsi_boot_kobj_release(void *data) +{ + struct beiscsi_hba *phba = data; + + scsi_host_put(phba->shost); +} + +static int beiscsi_boot_create_kset(struct beiscsi_hba *phba) +{ + struct boot_struct *bs = &phba->boot_struct; + struct iscsi_boot_kobj *boot_kobj; + + if (bs->boot_kset) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d: boot_kset already created\n"); + return 0; + } + + bs->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no); + if (!bs->boot_kset) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d: boot_kset alloc failed\n"); + return -ENOMEM; + } + + /* get shost ref because the show function will refer phba */ + if (!scsi_host_get(phba->shost)) + goto free_kset; + + boot_kobj = iscsi_boot_create_target(bs->boot_kset, 0, phba, + beiscsi_show_boot_tgt_info, + beiscsi_tgt_get_attr_visibility, + beiscsi_boot_kobj_release); + if (!boot_kobj) + goto put_shost; + + if (!scsi_host_get(phba->shost)) + goto free_kset; + + boot_kobj = iscsi_boot_create_initiator(bs->boot_kset, 0, phba, + beiscsi_show_boot_ini_info, + beiscsi_ini_get_attr_visibility, + beiscsi_boot_kobj_release); + if (!boot_kobj) + goto put_shost; + + if (!scsi_host_get(phba->shost)) + goto free_kset; + + boot_kobj = iscsi_boot_create_ethernet(bs->boot_kset, 0, phba, + beiscsi_show_boot_eth_info, + beiscsi_eth_get_attr_visibility, + beiscsi_boot_kobj_release); + if (!boot_kobj) + goto put_shost; + + return 0; + +put_shost: + scsi_host_put(phba->shost); +free_kset: + iscsi_boot_destroy_kset(bs->boot_kset); + bs->boot_kset = NULL; + return -ENOMEM; +} + +static void beiscsi_boot_work(struct work_struct *work) +{ + struct beiscsi_hba *phba = + container_of(work, struct beiscsi_hba, boot_work); + struct boot_struct *bs = &phba->boot_struct; + unsigned int tag = 0; + + if (beiscsi_hba_in_error(phba)) + return; + + beiscsi_log(phba, KERN_INFO, + BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, + "BM_%d : %s action %d\n", + __func__, phba->boot_struct.action); + + switch (phba->boot_struct.action) { + case BEISCSI_BOOT_REOPEN_SESS: + tag = beiscsi_boot_reopen_sess(phba); + break; + case BEISCSI_BOOT_GET_SHANDLE: + tag = __beiscsi_boot_get_shandle(phba, 1); + break; + case BEISCSI_BOOT_GET_SINFO: + tag = beiscsi_boot_get_sinfo(phba); + break; + case BEISCSI_BOOT_LOGOUT_SESS: + tag = beiscsi_boot_logout_sess(phba); + break; + case BEISCSI_BOOT_CREATE_KSET: + beiscsi_boot_create_kset(phba); + /** + * updated boot_kset is made visible to all before + * ending the boot work. + */ + mb(); + clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state); + return; + } + if (!tag) { + if (bs->retry--) + schedule_work(&phba->boot_work); + else + clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state); + } +} + static void be_eqd_update(struct beiscsi_hba *phba) { struct be_set_eqd set_eqd[MAX_CPUS]; @@ -5405,17 +5393,6 @@ static void be_eqd_update(struct beiscsi_hba *phba) } } -static void be_check_boot_session(struct beiscsi_hba *phba) -{ - if (beiscsi_hba_in_error(phba)) - return; - - if (beiscsi_setup_boot_info(phba)) - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Could not set up " - "iSCSI boot info on async event.\n"); -} - /* * beiscsi_hw_health_check()- Check adapter health * @work: work item to check HW health @@ -5431,17 +5408,6 @@ beiscsi_hw_health_check(struct work_struct *work) be_eqd_update(phba); - if (test_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state)) { - if ((phba->get_boot > 0) && (!phba->boot_kset)) { - phba->get_boot--; - if (!(phba->get_boot % BE_GET_BOOT_TO)) - be_check_boot_session(phba); - } else { - clear_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state); - phba->get_boot = 0; - } - } - beiscsi_ue_detect(phba); schedule_delayed_work(&phba->beiscsi_hw_check_task, @@ -5607,6 +5573,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, struct hwi_controller *phwi_ctrlr; struct hwi_context_memory *phwi_context; struct be_eq_obj *pbe_eq; + unsigned int s_handle; int ret = 0, i; ret = beiscsi_enable_pci(pcidev); @@ -5769,14 +5736,17 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, if (iscsi_host_add(phba->shost, &phba->pcidev->dev)) goto free_blkenbld; - if (beiscsi_setup_boot_info(phba)) - /* - * log error but continue, because we may not be using - * iscsi boot. + INIT_WORK(&phba->boot_work, beiscsi_boot_work); + ret = beiscsi_boot_get_shandle(phba, &s_handle); + if (ret > 0) { + beiscsi_start_boot_work(phba, s_handle); + /** + * Set this bit after starting the work to let + * probe handle it first. + * ASYNC event can too schedule this work. */ - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Could not set up " - "iSCSI boot info.\n"); + set_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state); + } beiscsi_iface_create_default(phba); schedule_delayed_work(&phba->beiscsi_hw_check_task, diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 8ab16516ae9a..780c3fc16ec6 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -96,11 +96,6 @@ #define MAX_CMD_SZ 65536 #define IIOC_SCSI_DATA 0x05 /* Write Operation */ -#define INVALID_SESS_HANDLE 0xFFFFFFFF - -#define BE_GET_BOOT_RETRIES 45 -#define BE_GET_BOOT_TO 20 - /** * hardware needs the async PDU buffers to be posted in multiples of 8 * So have atleast 8 of them by default @@ -378,7 +373,6 @@ struct beiscsi_hba { struct ulp_cid_info *cid_array_info[BEISCSI_ULP_COUNT]; struct iscsi_endpoint **ep_array; struct beiscsi_conn **conn_table; - struct iscsi_boot_kset *boot_kset; struct Scsi_Host *shost; struct iscsi_iface *ipv4_iface; struct iscsi_iface *ipv6_iface; @@ -410,16 +404,16 @@ struct beiscsi_hba { #define BEISCSI_HBA_RUNNING 0 #define BEISCSI_HBA_LINK_UP 1 #define BEISCSI_HBA_BOOT_FOUND 2 -#define BEISCSI_HBA_PCI_ERR 3 -#define BEISCSI_HBA_FW_TIMEOUT 4 -#define BEISCSI_HBA_IN_UE 5 +#define BEISCSI_HBA_BOOT_WORK 3 +#define BEISCSI_HBA_PCI_ERR 4 +#define BEISCSI_HBA_FW_TIMEOUT 5 +#define BEISCSI_HBA_IN_UE 6 /* error bits */ #define BEISCSI_HBA_IN_ERR ((1 << BEISCSI_HBA_PCI_ERR) | \ (1 << BEISCSI_HBA_FW_TIMEOUT) | \ (1 << BEISCSI_HBA_IN_UE)) u8 optic_state; - int get_boot; struct delayed_work beiscsi_hw_check_task; bool mac_addr_set; @@ -432,7 +426,6 @@ struct beiscsi_hba { struct be_ctrl_info ctrl; unsigned int generation; unsigned int interface_handle; - struct mgmt_session_info boot_sess; struct invalidate_command_table inv_tbl[128]; struct be_aic_obj aic_obj[MAX_CPUS]; @@ -441,6 +434,22 @@ struct beiscsi_hba { struct scatterlist *sg, uint32_t num_sg, uint32_t xferlen, uint32_t writedir); + struct boot_struct { + int retry; + unsigned int tag; + unsigned int s_handle; + struct be_dma_mem nonemb_cmd; + enum { + BEISCSI_BOOT_REOPEN_SESS = 1, + BEISCSI_BOOT_GET_SHANDLE, + BEISCSI_BOOT_GET_SINFO, + BEISCSI_BOOT_LOGOUT_SESS, + BEISCSI_BOOT_CREATE_KSET, + } action; + struct mgmt_session_info boot_sess; + struct iscsi_boot_kset *boot_kset; + } boot_struct; + struct work_struct boot_work; }; #define beiscsi_hba_in_error(phba) ((phba)->state & BEISCSI_HBA_IN_ERR) @@ -1066,6 +1075,8 @@ struct hwi_context_memory { struct hwi_async_pdu_context *pasync_ctx[BEISCSI_ULP_COUNT]; }; +void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle); + /* Logging related definitions */ #define BEISCSI_LOG_INIT 0x0001 /* Initialization events */ #define BEISCSI_LOG_MBOX 0x0002 /* Mailbox Events */ diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 4f2194e324ae..756e7ae33e2c 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -186,120 +186,6 @@ int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, return tag; } -/** - * mgmt_reopen_session()- Reopen a session based on reopen_type - * @phba: Device priv structure instance - * @reopen_type: Type of reopen_session FW should do. - * @sess_handle: Session Handle of the session to be re-opened - * - * return - * the TAG used for MBOX Command - * - **/ -unsigned int mgmt_reopen_session(struct beiscsi_hba *phba, - unsigned int reopen_type, - unsigned int sess_handle) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct be_cmd_reopen_session_req *req; - unsigned int tag; - - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : In bescsi_get_boot_target\n"); - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return 0; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, - OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS, - sizeof(struct be_cmd_reopen_session_resp)); - - /* set the reopen_type,sess_handle */ - req->reopen_type = reopen_type; - req->session_handle = sess_handle; - - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - return tag; -} - -unsigned int mgmt_get_boot_target(struct beiscsi_hba *phba) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct be_cmd_get_boot_target_req *req; - unsigned int tag; - - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : In bescsi_get_boot_target\n"); - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return 0; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, - OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET, - sizeof(struct be_cmd_get_boot_target_resp)); - - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - return tag; -} - -unsigned int mgmt_get_session_info(struct beiscsi_hba *phba, - u32 boot_session_handle, - struct be_dma_mem *nonemb_cmd) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - unsigned int tag; - struct be_cmd_get_session_req *req; - struct be_cmd_get_session_resp *resp; - struct be_sge *sge; - - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : In beiscsi_get_session_info\n"); - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return 0; - } - - nonemb_cmd->size = sizeof(*resp); - req = nonemb_cmd->va; - memset(req, 0, sizeof(*req)); - sge = nonembedded_sgl(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, - OPCODE_ISCSI_INI_SESSION_GET_A_SESSION, - sizeof(*resp)); - req->session_handle = boot_session_handle; - sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma)); - sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF); - sge->len = cpu_to_le32(nonemb_cmd->size); - - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - return tag; -} - /** * mgmt_get_port_name()- Get port name for the function * @ctrl: ptr to Ctrl Info @@ -1419,87 +1305,315 @@ unsigned int be_cmd_get_initname(struct beiscsi_hba *phba) return tag; } +static void beiscsi_boot_process_compl(struct beiscsi_hba *phba, + unsigned int tag) +{ + struct be_cmd_get_boot_target_resp *boot_resp; + struct be_cmd_resp_logout_fw_sess *logo_resp; + struct be_cmd_get_session_resp *sess_resp; + struct be_mcc_wrb *wrb; + struct boot_struct *bs; + int boot_work, status; + + if (!test_bit(BEISCSI_HBA_BOOT_WORK, &phba->state)) { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : %s no boot work %lx\n", + __func__, phba->state); + return; + } + + if (phba->boot_struct.tag != tag) { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : %s tag mismatch %d:%d\n", + __func__, tag, phba->boot_struct.tag); + return; + } + bs = &phba->boot_struct; + boot_work = 1; + status = 0; + switch (bs->action) { + case BEISCSI_BOOT_REOPEN_SESS: + status = __beiscsi_mcc_compl_status(phba, tag, NULL, NULL); + if (!status) + bs->action = BEISCSI_BOOT_GET_SHANDLE; + else + bs->retry--; + break; + case BEISCSI_BOOT_GET_SHANDLE: + status = __beiscsi_mcc_compl_status(phba, tag, &wrb, NULL); + if (!status) { + boot_resp = embedded_payload(wrb); + bs->s_handle = boot_resp->boot_session_handle; + } + if (bs->s_handle == BE_BOOT_INVALID_SHANDLE) { + bs->action = BEISCSI_BOOT_REOPEN_SESS; + bs->retry--; + } else { + bs->action = BEISCSI_BOOT_GET_SINFO; + } + break; + case BEISCSI_BOOT_GET_SINFO: + status = __beiscsi_mcc_compl_status(phba, tag, NULL, + &bs->nonemb_cmd); + if (!status) { + sess_resp = bs->nonemb_cmd.va; + memcpy(&bs->boot_sess, &sess_resp->session_info, + sizeof(struct mgmt_session_info)); + bs->action = BEISCSI_BOOT_LOGOUT_SESS; + } else { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : get boot session info error : 0x%x\n", + status); + boot_work = 0; + } + pci_free_consistent(phba->ctrl.pdev, bs->nonemb_cmd.size, + bs->nonemb_cmd.va, bs->nonemb_cmd.dma); + bs->nonemb_cmd.va = NULL; + break; + case BEISCSI_BOOT_LOGOUT_SESS: + status = __beiscsi_mcc_compl_status(phba, tag, &wrb, NULL); + if (!status) { + logo_resp = embedded_payload(wrb); + if (logo_resp->session_status != BE_SESS_STATUS_CLOSE) { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : FW boot session logout error : 0x%x\n", + logo_resp->session_status); + } + } + /* continue to create boot_kset even if logout failed? */ + bs->action = BEISCSI_BOOT_CREATE_KSET; + break; + default: + break; + } + + /* clear the tag so no other completion matches this tag */ + bs->tag = 0; + if (!bs->retry) { + boot_work = 0; + __beiscsi_log(phba, KERN_ERR, + "BG_%d : failed to setup boot target: status %d action %d\n", + status, bs->action); + } + if (!boot_work) { + /* wait for next event to start boot_work */ + clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state); + return; + } + schedule_work(&phba->boot_work); +} + /** - * be_mgmt_get_boot_shandle()- Get the session handle + * beiscsi_boot_logout_sess()- Logout from boot FW session + * @phba: Device priv structure instance + * + * return + * the TAG used for MBOX Command + * + */ +unsigned int beiscsi_boot_logout_sess(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb; + struct be_cmd_req_logout_fw_sess *req; + unsigned int tag; + + mutex_lock(&ctrl->mbox_lock); + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } + + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET, + sizeof(struct be_cmd_req_logout_fw_sess)); + /* Use the session handle copied into boot_sess */ + req->session_handle = phba->boot_struct.boot_sess.session_handle; + + phba->boot_struct.tag = tag; + set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state); + ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl; + + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + + return tag; +} +/** + * beiscsi_boot_reopen_sess()- Reopen boot session + * @phba: Device priv structure instance + * + * return + * the TAG used for MBOX Command + * + **/ +unsigned int beiscsi_boot_reopen_sess(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb; + struct be_cmd_reopen_session_req *req; + unsigned int tag; + + mutex_lock(&ctrl->mbox_lock); + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } + + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS, + sizeof(struct be_cmd_reopen_session_resp)); + req->reopen_type = BE_REOPEN_BOOT_SESSIONS; + req->session_handle = BE_BOOT_INVALID_SHANDLE; + + phba->boot_struct.tag = tag; + set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state); + ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl; + + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + return tag; +} + + +/** + * beiscsi_boot_get_sinfo()- Get boot session info + * @phba: device priv structure instance + * + * Fetches the boot_struct.s_handle info from FW. + * return + * the TAG used for MBOX Command + * + **/ +unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_cmd_get_session_resp *resp; + struct be_cmd_get_session_req *req; + struct be_dma_mem *nonemb_cmd; + struct be_mcc_wrb *wrb; + struct be_sge *sge; + unsigned int tag; + + mutex_lock(&ctrl->mbox_lock); + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } + + nonemb_cmd = &phba->boot_struct.nonemb_cmd; + nonemb_cmd->size = sizeof(*resp); + nonemb_cmd->va = pci_alloc_consistent(phba->ctrl.pdev, + sizeof(nonemb_cmd->size), + &nonemb_cmd->dma); + if (!nonemb_cmd->va) + return 0; + + req = nonemb_cmd->va; + memset(req, 0, sizeof(*req)); + sge = nonembedded_sgl(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_SESSION_GET_A_SESSION, + sizeof(*resp)); + req->session_handle = phba->boot_struct.s_handle; + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd->size); + + phba->boot_struct.tag = tag; + set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state); + ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl; + + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + return tag; +} + +unsigned int __beiscsi_boot_get_shandle(struct beiscsi_hba *phba, int async) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb; + struct be_cmd_get_boot_target_req *req; + unsigned int tag; + + mutex_lock(&ctrl->mbox_lock); + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } + + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET, + sizeof(struct be_cmd_get_boot_target_resp)); + + if (async) { + phba->boot_struct.tag = tag; + set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state); + ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl; + } + + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + return tag; +} + +/** + * beiscsi_boot_get_shandle()- Get boot session handle * @phba: device priv structure instance * @s_handle: session handle returned for boot session. * - * Get the boot target session handle. In case of - * crashdump mode driver has to issue and MBX Cmd - * for FW to login to boot target - * * return - * Success: 0 - * Failure: Non-Zero value + * Success: 1 + * Failure: negative * **/ -int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba, - unsigned int *s_handle) +int beiscsi_boot_get_shandle(struct beiscsi_hba *phba, unsigned int *s_handle) { struct be_cmd_get_boot_target_resp *boot_resp; struct be_mcc_wrb *wrb; unsigned int tag; - uint8_t boot_retry = 3; int rc; - do { - /* Get the Boot Target Session Handle and Count*/ - tag = mgmt_get_boot_target(phba); - if (!tag) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT, - "BG_%d : Getting Boot Target Info Failed\n"); - return -EAGAIN; - } + *s_handle = BE_BOOT_INVALID_SHANDLE; + /* get configured boot session count and handle */ + tag = __beiscsi_boot_get_shandle(phba, 0); + if (!tag) { + beiscsi_log(phba, KERN_ERR, + BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT, + "BG_%d : Getting Boot Target Info Failed\n"); + return -EAGAIN; + } - rc = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); - if (rc) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : MBX CMD get_boot_target Failed\n"); - return -EBUSY; - } + rc = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); + if (rc) { + beiscsi_log(phba, KERN_ERR, + BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, + "BG_%d : MBX CMD get_boot_target Failed\n"); + return -EBUSY; + } - boot_resp = embedded_payload(wrb); + boot_resp = embedded_payload(wrb); + /* check if there are any boot targets configured */ + if (!boot_resp->boot_session_count) { + __beiscsi_log(phba, KERN_INFO, + "BG_%d : No boot targets configured\n"); + return -ENXIO; + } - /* Check if the there are any Boot targets configured */ - if (!boot_resp->boot_session_count) { - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d ;No boot targets configured\n"); - return -ENXIO; - } - - /* FW returns the session handle of the boot session */ - if (boot_resp->boot_session_handle != INVALID_SESS_HANDLE) { - *s_handle = boot_resp->boot_session_handle; - return 0; - } - - /* Issue MBX Cmd to FW to login to the boot target */ - tag = mgmt_reopen_session(phba, BE_REOPEN_BOOT_SESSIONS, - INVALID_SESS_HANDLE); - if (!tag) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : mgmt_reopen_session Failed\n"); - return -EAGAIN; - } - - rc = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); - if (rc) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : mgmt_reopen_session Failed"); - return rc; - } - } while (--boot_retry); - - /* Couldn't log into the boot target */ - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : Login to Boot Target Failed\n"); - return -ENXIO; + /* only if FW has logged in to the boot target, s_handle is valid */ + *s_handle = boot_resp->boot_session_handle; + return 1; } /** @@ -1809,70 +1923,3 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, (params->dw[offsetof(struct amap_beiscsi_offload_params, exp_statsn) / 32] + 1)); } - -/** - * beiscsi_logout_fw_sess()- Firmware Session Logout - * @phba: Device priv structure instance - * @fw_sess_handle: FW session handle - * - * Logout from the FW established sessions. - * returns - * Success: 0 - * Failure: Non-Zero Value - * - */ -int beiscsi_logout_fw_sess(struct beiscsi_hba *phba, - uint32_t fw_sess_handle) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct be_cmd_req_logout_fw_sess *req; - struct be_cmd_resp_logout_fw_sess *resp; - unsigned int tag; - int rc; - - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : In bescsi_logout_fwboot_sess\n"); - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : MBX Tag Failure\n"); - return -EINVAL; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, - OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET, - sizeof(struct be_cmd_req_logout_fw_sess)); - - /* Set the session handle */ - req->session_handle = fw_sess_handle; - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - - rc = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); - if (rc) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : MBX CMD FW_SESSION_LOGOUT_TARGET Failed\n"); - return -EBUSY; - } - - resp = embedded_payload(wrb); - if (resp->session_status != - BEISCSI_MGMT_SESSION_CLOSE) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : FW_SESSION_LOGOUT_TARGET resp : 0x%x\n", - resp->session_status); - rc = -EINVAL; - } - - return rc; -} diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index 294b740805a6..25053483cf28 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -282,16 +282,6 @@ int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type); int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, u8 *ip, u8 *subnet); -unsigned int mgmt_get_boot_target(struct beiscsi_hba *phba); - -unsigned int mgmt_reopen_session(struct beiscsi_hba *phba, - unsigned int reopen_type, - unsigned sess_handle); - -unsigned int mgmt_get_session_info(struct beiscsi_hba *phba, - u32 boot_session_handle, - struct be_dma_mem *nonemb_cmd); - int mgmt_get_nic_conf(struct beiscsi_hba *phba, struct be_cmd_get_nic_conf_resp *mac); @@ -303,13 +293,20 @@ int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw); -int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba, - unsigned int *s_handle); - unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba); int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); +unsigned int beiscsi_boot_logout_sess(struct beiscsi_hba *phba); + +unsigned int beiscsi_boot_reopen_sess(struct beiscsi_hba *phba); + +unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba); + +unsigned int __beiscsi_boot_get_shandle(struct beiscsi_hba *phba, int async); + +int beiscsi_boot_get_shandle(struct beiscsi_hba *phba, unsigned int *s_handle); + ssize_t beiscsi_drvr_ver_disp(struct device *dev, struct device_attribute *attr, char *buf); From 10bcd47dff496206de68223aeb1a581bccad03d3 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:13 +0530 Subject: [PATCH 044/173] scsi: be2iscsi: Fix to add timer for UE detection UE detection in health check is done in a work scheduled in global wq. UE caused due to transient parity errors are recoverable and reported within 1s. If this check for TPE gets delayed, PF0 might initiate soft-reset and then status of UE recoverable is lost. Handle UE detection in timer routine. Move out EQ delay update work from health check. Make the IOCTL for EQ delay update non-blocking as the completion status is ignored. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be.h | 9 ++-- drivers/scsi/be2iscsi/be_cmds.c | 7 +++ drivers/scsi/be2iscsi/be_cmds.h | 2 +- drivers/scsi/be2iscsi/be_main.c | 96 ++++++++++++++++++--------------- drivers/scsi/be2iscsi/be_main.h | 7 ++- drivers/scsi/be2iscsi/be_mgmt.c | 11 ++-- 6 files changed, 78 insertions(+), 54 deletions(-) diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h index 454002d98bcd..bb4042c9acfb 100644 --- a/drivers/scsi/be2iscsi/be.h +++ b/drivers/scsi/be2iscsi/be.h @@ -89,7 +89,7 @@ struct be_aic_obj { /* Adaptive interrupt coalescing (AIC) info */ u32 max_eqd; /* in usecs */ u32 prev_eqd; /* in usecs */ u32 et_eqd; /* configured val when aic is off */ - ulong jiffs; + ulong jiffies; u64 eq_prev; /* Used to calculate eqe */ }; @@ -111,9 +111,10 @@ struct be_mcc_obj { struct beiscsi_mcc_tag_state { unsigned long tag_state; -#define MCC_TAG_STATE_RUNNING 1 -#define MCC_TAG_STATE_TIMEOUT 2 -#define MCC_TAG_STATE_ASYNC 3 +#define MCC_TAG_STATE_RUNNING 0 +#define MCC_TAG_STATE_TIMEOUT 1 +#define MCC_TAG_STATE_ASYNC 2 +#define MCC_TAG_STATE_IGNORE 3 void (*cbfn)(struct beiscsi_hba *, unsigned int); struct be_dma_mem tag_mem_state; }; diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 27d10cee0c40..7cb009e0030e 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -515,6 +515,13 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, return 0; } + if (test_bit(MCC_TAG_STATE_IGNORE, &ctrl->ptag_state[tag].tag_state)) { + /* just check completion status and free wrb */ + __beiscsi_mcc_compl_status(phba, tag, NULL, NULL); + free_mcc_wrb(ctrl, tag); + return 0; + } + wake_up_interruptible(&ctrl->mcc_wait[tag]); return 0; } diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index 0510b6767341..6fb9673248b3 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -743,7 +743,7 @@ unsigned int be_cmd_get_initname(struct beiscsi_hba *phba); void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag); -int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *, +int beiscsi_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *, int num); int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, unsigned int tag, diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index eb4ce17a683d..3dd4f9d126ae 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -4999,8 +4999,9 @@ static void beiscsi_quiesce(struct beiscsi_hba *phba) if (phba->pcidev->irq) free_irq(phba->pcidev->irq, phba); pci_disable_msix(phba->pcidev); - cancel_delayed_work_sync(&phba->beiscsi_hw_check_task); + cancel_delayed_work_sync(&phba->eqd_update); cancel_work_sync(&phba->boot_work); + del_timer_sync(&phba->hw_check); for (i = 0; i < phba->num_cpus; i++) { pbe_eq = &phwi_context->be_eq[i]; @@ -5339,18 +5340,32 @@ static void beiscsi_boot_work(struct work_struct *work) } } -static void be_eqd_update(struct beiscsi_hba *phba) +static void beiscsi_hw_health_check(unsigned long ptr) { - struct be_set_eqd set_eqd[MAX_CPUS]; - struct be_aic_obj *aic; - struct be_eq_obj *pbe_eq; - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - int eqd, i, num = 0; - ulong now; - u32 pps, delta; - unsigned int tag; + struct beiscsi_hba *phba; + phba = (struct beiscsi_hba *)ptr; + beiscsi_ue_detect(phba); + if (test_bit(BEISCSI_HBA_IN_UE, &phba->state)) + return; + + mod_timer(&phba->hw_check, + jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); +} + +static void beiscsi_eqd_update_work(struct work_struct *work) +{ + struct hwi_context_memory *phwi_context; + struct be_set_eqd set_eqd[MAX_CPUS]; + struct hwi_controller *phwi_ctrlr; + struct be_eq_obj *pbe_eq; + struct beiscsi_hba *phba; + unsigned int pps, delta; + struct be_aic_obj *aic; + int eqd, i, num = 0; + unsigned long now; + + phba = container_of(work, struct beiscsi_hba, eqd_update.work); if (beiscsi_hba_in_error(phba)) return; @@ -5361,13 +5376,13 @@ static void be_eqd_update(struct beiscsi_hba *phba) aic = &phba->aic_obj[i]; pbe_eq = &phwi_context->be_eq[i]; now = jiffies; - if (!aic->jiffs || time_before(now, aic->jiffs) || + if (!aic->jiffies || time_before(now, aic->jiffies) || pbe_eq->cq_count < aic->eq_prev) { - aic->jiffs = now; + aic->jiffies = now; aic->eq_prev = pbe_eq->cq_count; continue; } - delta = jiffies_to_msecs(now - aic->jiffs); + delta = jiffies_to_msecs(now - aic->jiffies); pps = (((u32)(pbe_eq->cq_count - aic->eq_prev) * 1000) / delta); eqd = (pps / 1500) << 2; @@ -5376,7 +5391,7 @@ static void be_eqd_update(struct beiscsi_hba *phba) eqd = min_t(u32, eqd, phwi_context->max_eqd); eqd = max_t(u32, eqd, phwi_context->min_eqd); - aic->jiffs = now; + aic->jiffies = now; aic->eq_prev = pbe_eq->cq_count; if (eqd != aic->prev_eqd) { @@ -5386,32 +5401,12 @@ static void be_eqd_update(struct beiscsi_hba *phba) num++; } } - if (num) { - tag = be_cmd_modify_eq_delay(phba, set_eqd, num); - if (tag) - beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); - } -} + if (num) + /* completion of this is ignored */ + beiscsi_modify_eq_delay(phba, set_eqd, num); -/* - * beiscsi_hw_health_check()- Check adapter health - * @work: work item to check HW health - * - * Check if adapter in an unrecoverable state or not. - **/ -static void -beiscsi_hw_health_check(struct work_struct *work) -{ - struct beiscsi_hba *phba = - container_of(work, struct beiscsi_hba, - beiscsi_hw_check_task.work); - - be_eqd_update(phba); - - beiscsi_ue_detect(phba); - - schedule_delayed_work(&phba->beiscsi_hw_check_task, - msecs_to_jiffies(1000)); + schedule_delayed_work(&phba->eqd_update, + msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); } @@ -5560,6 +5555,11 @@ static void beiscsi_eeh_resume(struct pci_dev *pdev) hwi_enable_intr(phba); clear_bit(BEISCSI_HBA_PCI_ERR, &phba->state); + /* start hw_check timer and eqd_update work */ + schedule_delayed_work(&phba->eqd_update, + msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); + mod_timer(&phba->hw_check, + jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); return; ret_err: beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, @@ -5707,8 +5707,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, goto free_twq; } - INIT_DELAYED_WORK(&phba->beiscsi_hw_check_task, - beiscsi_hw_health_check); + INIT_DELAYED_WORK(&phba->eqd_update, beiscsi_eqd_update_work); phwi_ctrlr = phba->phwi_ctrlr; phwi_context = phwi_ctrlr->phwi_ctxt; @@ -5749,8 +5748,17 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, } beiscsi_iface_create_default(phba); - schedule_delayed_work(&phba->beiscsi_hw_check_task, - msecs_to_jiffies(1000)); + schedule_delayed_work(&phba->eqd_update, + msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); + /** + * Start UE detection here. UE before this will cause stall in probe + * and eventually fail the probe. + */ + init_timer(&phba->hw_check); + phba->hw_check.function = beiscsi_hw_health_check; + phba->hw_check.data = (unsigned long)phba; + mod_timer(&phba->hw_check, + jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, "\n\n\n BM_%d : SUCCESS - DRIVER LOADED\n\n\n"); diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 780c3fc16ec6..0d34ac611d2f 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -414,7 +414,12 @@ struct beiscsi_hba { (1 << BEISCSI_HBA_IN_UE)) u8 optic_state; - struct delayed_work beiscsi_hw_check_task; + struct delayed_work eqd_update; + /* update EQ delay timer every 1000ms */ +#define BEISCSI_EQD_UPDATE_INTERVAL 1000 + struct timer_list hw_check; + /* check for UE every 1000ms */ +#define BEISCSI_UE_DETECT_INTERVAL 1000 bool mac_addr_set; u8 mac_address[ETH_ALEN]; diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 756e7ae33e2c..60a116388467 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -97,7 +97,7 @@ static const char * const desc_ue_status_hi[] = { }; /* - * beiscsi_ue_detec()- Detect Unrecoverable Error on adapter + * beiscsi_ue_detect()- Detect Unrecoverable Error on adapter * @phba: Driver priv structure * * Read registers linked to UE and check for the UE status @@ -152,8 +152,9 @@ void beiscsi_ue_detect(struct beiscsi_hba *phba) } } -int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, - struct be_set_eqd *set_eqd, int num) +int beiscsi_modify_eq_delay(struct beiscsi_hba *phba, + struct be_set_eqd *set_eqd, + int num) { struct be_ctrl_info *ctrl = &phba->ctrl; struct be_mcc_wrb *wrb; @@ -171,7 +172,7 @@ int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, req = embedded_payload(wrb); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req)); + OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req)); req->num_eq = cpu_to_le32(num); for (i = 0; i < num; i++) { @@ -181,6 +182,8 @@ int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, cpu_to_le32(set_eqd[i].delay_multiplier); } + /* ignore the completion of this mbox command */ + set_bit(MCC_TAG_STATE_IGNORE, &ctrl->ptag_state[tag].tag_state); be_mcc_notify(phba, tag); mutex_unlock(&ctrl->mbox_lock); return tag; From 6694095b5a28c54d9fd114997e483cdc47a2e792 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:14 +0530 Subject: [PATCH 045/173] scsi: be2iscsi: Add IOCTL to check UER supported BE3 and SH cards can recover from transient parity errors treated earlier as unrecoverable errors. Add IOCTL to query FW support for this feature. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 58 +++++++++++++++++++++++++-------- drivers/scsi/be2iscsi/be_cmds.h | 34 +++++++++++++++++++ drivers/scsi/be2iscsi/be_main.c | 1 + drivers/scsi/be2iscsi/be_main.h | 13 +++++--- drivers/scsi/be2iscsi/be_mgmt.c | 2 +- 5 files changed, 89 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 7cb009e0030e..a246abebb1de 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -277,11 +277,10 @@ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, static int beiscsi_process_mbox_compl(struct be_ctrl_info *ctrl, struct be_mcc_compl *compl) { - u16 compl_status, extd_status; struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev); struct be_cmd_req_hdr *hdr = embedded_payload(wrb); - struct be_cmd_resp_hdr *resp_hdr; + u16 compl_status, extd_status; /** * To check if valid bit is set, check the entire word as we don't know @@ -315,14 +314,7 @@ static int beiscsi_process_mbox_compl(struct be_ctrl_info *ctrl, beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, "BC_%d : error in cmd completion: Subsystem : %d Opcode : %d status(compl/extd)=%d/%d\n", hdr->subsystem, hdr->opcode, compl_status, extd_status); - - if (compl_status == MCC_STATUS_INSUFFICIENT_BUFFER) { - /* if status is insufficient buffer, check the length */ - resp_hdr = (struct be_cmd_resp_hdr *) hdr; - if (resp_hdr->response_length) - return 0; - } - return -EINVAL; + return compl_status; } static void beiscsi_process_async_link(struct beiscsi_hba *phba, @@ -507,10 +499,8 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, if (ctrl->ptag_state[tag].cbfn) ctrl->ptag_state[tag].cbfn(phba, tag); else - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_MBOX | BEISCSI_LOG_INIT | - BEISCSI_LOG_CONFIG, - "BC_%d : MBX ASYNC command with no callback\n"); + __beiscsi_log(phba, KERN_ERR, + "BC_%d : MBX ASYNC command with no callback\n"); free_mcc_wrb(ctrl, tag); return 0; } @@ -1371,3 +1361,43 @@ int be_cmd_set_vlan(struct beiscsi_hba *phba, return tag; } + +int beiscsi_set_uer_feature(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_cmd_set_features *ioctl; + struct be_mcc_wrb *wrb; + int ret = 0; + + mutex_lock(&ctrl->mbox_lock); + wrb = wrb_from_mbox(&ctrl->mbox_mem); + memset(wrb, 0, sizeof(*wrb)); + ioctl = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0); + be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_SET_FEATURES, + EMBED_MBX_MAX_PAYLOAD_SIZE); + ioctl->feature = BE_CMD_SET_FEATURE_UER; + ioctl->param_len = sizeof(ioctl->param.req); + ioctl->param.req.uer = BE_CMD_UER_SUPP_BIT; + ret = be_mbox_notify(ctrl); + if (!ret) { + phba->ue2rp = ioctl->param.resp.ue2rp; + set_bit(BEISCSI_HBA_UER_SUPP, &phba->state); + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : HBA error recovery supported\n"); + } else { + /** + * Check "MCC_STATUS_INVALID_LENGTH" for SKH. + * Older FW versions return this error. + */ + if (ret == MCC_STATUS_ILLEGAL_REQUEST || + ret == MCC_STATUS_INVALID_LENGTH) + __beiscsi_log(phba, KERN_INFO, + "BG_%d : HBA error recovery not supported\n"); + } + + mutex_unlock(&ctrl->mbox_lock); + return ret; +} diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index 6fb9673248b3..f1356c941e44 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -57,6 +57,7 @@ struct be_mcc_wrb { #define MCC_STATUS_ILLEGAL_REQUEST 0x2 #define MCC_STATUS_ILLEGAL_FIELD 0x3 #define MCC_STATUS_INSUFFICIENT_BUFFER 0x4 +#define MCC_STATUS_INVALID_LENGTH 0x74 #define CQE_STATUS_COMPL_MASK 0xFFFF #define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */ @@ -217,6 +218,7 @@ struct be_mcc_mailbox { #define OPCODE_COMMON_QUERY_FIRMWARE_CONFIG 58 #define OPCODE_COMMON_FUNCTION_RESET 61 #define OPCODE_COMMON_GET_PORT_NAME 77 +#define OPCODE_COMMON_SET_FEATURES 191 /** * LIST of opcodes that are common between Initiator and Target @@ -712,6 +714,8 @@ struct be_cmd_get_nic_conf_resp { u8 mac_address[ETH_ALEN]; } __packed; +/******************** Get HBA NAME *******************/ + #define BEISCSI_ALIAS_LEN 32 struct be_cmd_hba_name { @@ -722,6 +726,34 @@ struct be_cmd_hba_name { u8 initiator_alias[BEISCSI_ALIAS_LEN]; } __packed; +/******************** COMMON SET Features *******************/ +#define BE_CMD_SET_FEATURE_UER 0x10 +#define BE_CMD_UER_SUPP_BIT 0x1 +struct be_uer_req { + u32 uer; + u32 rsvd; +}; + +struct be_uer_resp { + u32 uer; + u16 ue2rp; + u16 ue2sr; +}; + +struct be_cmd_set_features { + union { + struct be_cmd_req_hdr req_hdr; + struct be_cmd_resp_hdr resp_hdr; + } h; + u32 feature; + u32 param_len; + union { + struct be_uer_req req; + struct be_uer_resp resp; + u32 rsvd[2]; + } param; +} __packed; + int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_queue_info *eq, int eq_delay); @@ -795,6 +827,8 @@ int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, /* Configuration Functions */ int be_cmd_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); +int beiscsi_set_uer_feature(struct beiscsi_hba *phba); + struct be_default_pdu_context { u32 dw[4]; } __packed; diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 3dd4f9d126ae..55dc6439ae64 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -5660,6 +5660,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, } mgmt_get_port_name(&phba->ctrl, phba); beiscsi_get_params(phba); + beiscsi_set_uer_feature(phba); if (enable_msix) find_num_cpus(phba); diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 0d34ac611d2f..0a5de01c3883 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -405,13 +405,17 @@ struct beiscsi_hba { #define BEISCSI_HBA_LINK_UP 1 #define BEISCSI_HBA_BOOT_FOUND 2 #define BEISCSI_HBA_BOOT_WORK 3 -#define BEISCSI_HBA_PCI_ERR 4 -#define BEISCSI_HBA_FW_TIMEOUT 5 -#define BEISCSI_HBA_IN_UE 6 +#define BEISCSI_HBA_UER_SUPP 4 +#define BEISCSI_HBA_PCI_ERR 5 +#define BEISCSI_HBA_FW_TIMEOUT 6 +#define BEISCSI_HBA_IN_UE 7 +#define BEISCSI_HBA_IN_TPE 8 + /* error bits */ #define BEISCSI_HBA_IN_ERR ((1 << BEISCSI_HBA_PCI_ERR) | \ (1 << BEISCSI_HBA_FW_TIMEOUT) | \ - (1 << BEISCSI_HBA_IN_UE)) + (1 << BEISCSI_HBA_IN_UE) | \ + (1 << BEISCSI_HBA_IN_TPE)) u8 optic_state; struct delayed_work eqd_update; @@ -420,6 +424,7 @@ struct beiscsi_hba { struct timer_list hw_check; /* check for UE every 1000ms */ #define BEISCSI_UE_DETECT_INTERVAL 1000 + u32 ue2rp; bool mac_addr_set; u8 mac_address[ETH_ALEN]; diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 60a116388467..08d94b014459 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -128,7 +128,7 @@ void beiscsi_ue_detect(struct beiscsi_hba *phba) set_bit(BEISCSI_HBA_IN_UE, &phba->state); beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : Error detected on the adapter\n"); + "BG_%d : HBA error detected\n"); } if (ue_lo) { From 480195c267a4c9f9fcb490897278a41940759b4f Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:15 +0530 Subject: [PATCH 046/173] scsi: be2iscsi: Move functions to right files beiscsi_fail_session is defined in be_cmds.c: move it to be_iscsi.c Move card configuration commands to be_cmds.c. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 274 +++++++++++++++++++++++++++++-- drivers/scsi/be2iscsi/be_cmds.h | 20 +-- drivers/scsi/be2iscsi/be_iscsi.c | 10 ++ drivers/scsi/be2iscsi/be_iscsi.h | 2 + drivers/scsi/be2iscsi/be_main.c | 6 +- drivers/scsi/be2iscsi/be_mgmt.c | 261 ----------------------------- drivers/scsi/be2iscsi/be_mgmt.h | 15 +- 7 files changed, 293 insertions(+), 295 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index a246abebb1de..1ebb6ce12742 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -119,15 +119,6 @@ void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag) spin_unlock(&ctrl->mcc_lock); } -/** - * beiscsi_fail_session(): Closing session with appropriate error - * @cls_session: ptr to session - **/ -void beiscsi_fail_session(struct iscsi_cls_session *cls_session) -{ - iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); -} - /* * beiscsi_mcc_compl_status - Return the status of MCC completion * @phba: Driver private structure @@ -342,7 +333,7 @@ static void beiscsi_process_async_link(struct beiscsi_hba *phba, "BC_%d : Link Down on Port %d tag 0x%x\n", evt->physical_port, evt->event_tag); iscsi_host_for_each_session(phba->shost, - beiscsi_fail_session); + beiscsi_session_fail); } } @@ -638,7 +629,7 @@ static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl) * Success: 0 * Failure: Non-Zero **/ -int be_mbox_notify(struct be_ctrl_info *ctrl) +static int be_mbox_notify(struct be_ctrl_info *ctrl) { int status; u32 val = 0; @@ -1362,6 +1353,267 @@ int be_cmd_set_vlan(struct beiscsi_hba *phba, return tag; } +int beiscsi_check_supported_fw(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba) +{ + struct be_dma_mem nonemb_cmd; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mgmt_controller_attributes *req; + struct be_sge *sge = nonembedded_sgl(wrb); + int status = 0; + + nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev, + sizeof(struct be_mgmt_controller_attributes), + &nonemb_cmd.dma); + if (nonemb_cmd.va == NULL) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : pci_alloc_consistent failed in %s\n", + __func__); + return -ENOMEM; + } + nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes); + req = nonemb_cmd.va; + memset(req, 0, sizeof(*req)); + mutex_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req)); + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd.size); + status = be_mbox_notify(ctrl); + if (!status) { + struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va; + + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : Firmware Version of CMD : %s\n" + "Firmware Version is : %s\n" + "Developer Build, not performing version check...\n", + resp->params.hba_attribs + .flashrom_version_string, + resp->params.hba_attribs. + firmware_version_string); + + phba->fw_config.iscsi_features = + resp->params.hba_attribs.iscsi_features; + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BM_%d : phba->fw_config.iscsi_features = %d\n", + phba->fw_config.iscsi_features); + memcpy(phba->fw_ver_str, resp->params.hba_attribs. + firmware_version_string, BEISCSI_VER_STRLEN); + } else + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : Failed in beiscsi_check_supported_fw\n"); + mutex_unlock(&ctrl->mbox_lock); + if (nonemb_cmd.va) + pci_free_consistent(ctrl->pdev, nonemb_cmd.size, + nonemb_cmd.va, nonemb_cmd.dma); + + return status; +} + +/** + * beiscsi_get_fw_config()- Get the FW config for the function + * @ctrl: ptr to Ctrl Info + * @phba: ptr to the dev priv structure + * + * Get the FW config and resources available for the function. + * The resources are created based on the count received here. + * + * return + * Success: 0 + * Failure: Non-Zero Value + **/ +int beiscsi_get_fw_config(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_fw_cfg *pfw_cfg = embedded_payload(wrb); + uint32_t cid_count, icd_count; + int status = -EINVAL; + uint8_t ulp_num = 0; + + mutex_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*pfw_cfg), true, 0); + + be_cmd_hdr_prepare(&pfw_cfg->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, + EMBED_MBX_MAX_PAYLOAD_SIZE); + + if (be_mbox_notify(ctrl)) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : Failed in beiscsi_get_fw_config\n"); + goto fail_init; + } + + /* FW response formats depend on port id */ + phba->fw_config.phys_port = pfw_cfg->phys_port; + if (phba->fw_config.phys_port >= BEISCSI_PHYS_PORT_MAX) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : invalid physical port id %d\n", + phba->fw_config.phys_port); + goto fail_init; + } + + /* populate and check FW config against min and max values */ + if (!is_chip_be2_be3r(phba)) { + phba->fw_config.eqid_count = pfw_cfg->eqid_count; + phba->fw_config.cqid_count = pfw_cfg->cqid_count; + if (phba->fw_config.eqid_count == 0 || + phba->fw_config.eqid_count > 2048) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : invalid EQ count %d\n", + phba->fw_config.eqid_count); + goto fail_init; + } + if (phba->fw_config.cqid_count == 0 || + phba->fw_config.cqid_count > 4096) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : invalid CQ count %d\n", + phba->fw_config.cqid_count); + goto fail_init; + } + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : EQ_Count : %d CQ_Count : %d\n", + phba->fw_config.eqid_count, + phba->fw_config.cqid_count); + } + + /** + * Check on which all ULP iSCSI Protocol is loaded. + * Set the Bit for those ULP. This set flag is used + * at all places in the code to check on which ULP + * iSCSi Protocol is loaded + **/ + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { + if (pfw_cfg->ulp[ulp_num].ulp_mode & + BEISCSI_ULP_ISCSI_INI_MODE) { + set_bit(ulp_num, &phba->fw_config.ulp_supported); + + /* Get the CID, ICD and Chain count for each ULP */ + phba->fw_config.iscsi_cid_start[ulp_num] = + pfw_cfg->ulp[ulp_num].sq_base; + phba->fw_config.iscsi_cid_count[ulp_num] = + pfw_cfg->ulp[ulp_num].sq_count; + + phba->fw_config.iscsi_icd_start[ulp_num] = + pfw_cfg->ulp[ulp_num].icd_base; + phba->fw_config.iscsi_icd_count[ulp_num] = + pfw_cfg->ulp[ulp_num].icd_count; + + phba->fw_config.iscsi_chain_start[ulp_num] = + pfw_cfg->chain_icd[ulp_num].chain_base; + phba->fw_config.iscsi_chain_count[ulp_num] = + pfw_cfg->chain_icd[ulp_num].chain_count; + + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : Function loaded on ULP : %d\n" + "\tiscsi_cid_count : %d\n" + "\tiscsi_cid_start : %d\n" + "\t iscsi_icd_count : %d\n" + "\t iscsi_icd_start : %d\n", + ulp_num, + phba->fw_config. + iscsi_cid_count[ulp_num], + phba->fw_config. + iscsi_cid_start[ulp_num], + phba->fw_config. + iscsi_icd_count[ulp_num], + phba->fw_config. + iscsi_icd_start[ulp_num]); + } + } + + if (phba->fw_config.ulp_supported == 0) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : iSCSI initiator mode not set: ULP0 %x ULP1 %x\n", + pfw_cfg->ulp[BEISCSI_ULP0].ulp_mode, + pfw_cfg->ulp[BEISCSI_ULP1].ulp_mode); + goto fail_init; + } + + /** + * ICD is shared among ULPs. Use icd_count of any one loaded ULP + **/ + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) + if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) + break; + icd_count = phba->fw_config.iscsi_icd_count[ulp_num]; + if (icd_count == 0 || icd_count > 65536) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d: invalid ICD count %d\n", icd_count); + goto fail_init; + } + + cid_count = BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP0) + + BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP1); + if (cid_count == 0 || cid_count > 4096) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d: invalid CID count %d\n", cid_count); + goto fail_init; + } + + /** + * Check FW is dual ULP aware i.e. can handle either + * of the protocols. + */ + phba->fw_config.dual_ulp_aware = (pfw_cfg->function_mode & + BEISCSI_FUNC_DUA_MODE); + + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : DUA Mode : 0x%x\n", + phba->fw_config.dual_ulp_aware); + + /* all set, continue using this FW config */ + status = 0; +fail_init: + mutex_unlock(&ctrl->mbox_lock); + return status; +} + +/** + * beiscsi_get_port_name()- Get port name for the function + * @ctrl: ptr to Ctrl Info + * @phba: ptr to the dev priv structure + * + * Get the alphanumeric character for port + * + **/ +int beiscsi_get_port_name(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba) +{ + int ret = 0; + struct be_mcc_wrb *wrb; + struct be_cmd_get_port_name *ioctl; + + mutex_lock(&ctrl->mbox_lock); + wrb = wrb_from_mbox(&ctrl->mbox_mem); + memset(wrb, 0, sizeof(*wrb)); + ioctl = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0); + be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_GET_PORT_NAME, + EMBED_MBX_MAX_PAYLOAD_SIZE); + ret = be_mbox_notify(ctrl); + phba->port_name = 0; + if (!ret) { + phba->port_name = ioctl->p.resp.port_names >> + (phba->fw_config.phys_port * 8) & 0xff; + } else { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : GET_PORT_NAME ret 0x%x status 0x%x\n", + ret, ioctl->h.resp_hdr.status); + } + + if (phba->port_name == 0) + phba->port_name = '?'; + + mutex_unlock(&ctrl->mbox_lock); + return ret; +} + int beiscsi_set_uer_feature(struct beiscsi_hba *phba) { struct be_ctrl_info *ctrl = &phba->ctrl; diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index f1356c941e44..c9823b00b254 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -716,14 +716,13 @@ struct be_cmd_get_nic_conf_resp { /******************** Get HBA NAME *******************/ -#define BEISCSI_ALIAS_LEN 32 - struct be_cmd_hba_name { struct be_cmd_req_hdr hdr; u16 flags; u16 rsvd0; u8 initiator_name[ISCSI_NAME_LEN]; - u8 initiator_alias[BEISCSI_ALIAS_LEN]; +#define BE_INI_ALIAS_LEN 32 + u8 initiator_alias[BE_INI_ALIAS_LEN]; } __packed; /******************** COMMON SET Features *******************/ @@ -769,8 +768,7 @@ int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba, struct be_queue_info *cq); int be_poll_mcc(struct be_ctrl_info *ctrl); -int mgmt_check_supported_fw(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba); + unsigned int be_cmd_get_initname(struct beiscsi_hba *phba); void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag); @@ -799,9 +797,6 @@ void beiscsi_process_async_event(struct beiscsi_hba *phba, int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, struct be_mcc_compl *compl); - -int be_mbox_notify(struct be_ctrl_info *ctrl); - int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl, struct be_queue_info *cq, struct be_queue_info *dq, int length, @@ -827,6 +822,13 @@ int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, /* Configuration Functions */ int be_cmd_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); +int beiscsi_check_supported_fw(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba); + +int beiscsi_get_fw_config(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba); + +int beiscsi_get_port_name(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba); + int beiscsi_set_uer_feature(struct beiscsi_hba *phba); struct be_default_pdu_context { @@ -1427,6 +1429,4 @@ void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr, u8 subsystem, u8 opcode, int cmd_len); - -void beiscsi_fail_session(struct iscsi_cls_session *cls_session); #endif /* !BEISCSI_CMDS_H */ diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index ddb458abbf59..677491a4b0b4 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -117,6 +117,16 @@ void beiscsi_session_destroy(struct iscsi_cls_session *cls_session) iscsi_session_teardown(cls_session); } +/** + * beiscsi_session_fail(): Closing session with appropriate error + * @cls_session: ptr to session + **/ +void beiscsi_session_fail(struct iscsi_cls_session *cls_session) +{ + iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); +} + + /** * beiscsi_conn_create - create an instance of iscsi connection * @cls_session: ptr to iscsi_cls_session diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h index 0089e67badf8..2a4097a17ad0 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.h +++ b/drivers/scsi/be2iscsi/be_iscsi.h @@ -50,6 +50,8 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, void beiscsi_session_destroy(struct iscsi_cls_session *cls_session); +void beiscsi_session_fail(struct iscsi_cls_session *cls_session); + struct iscsi_cls_conn *beiscsi_conn_create(struct iscsi_cls_session *cls_session, uint32_t cid); diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 55dc6439ae64..77ecd6c48207 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -3715,7 +3715,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) if (status != 0) goto error; - status = mgmt_check_supported_fw(ctrl, phba); + status = beiscsi_check_supported_fw(ctrl, phba); if (status != 0) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Unsupported fw version\n"); @@ -5652,13 +5652,13 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, spin_lock_init(&phba->io_sgl_lock); spin_lock_init(&phba->mgmt_sgl_lock); spin_lock_init(&phba->async_pdu_lock); - ret = mgmt_get_fw_config(&phba->ctrl, phba); + ret = beiscsi_get_fw_config(&phba->ctrl, phba); if (ret != 0) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Error getting fw config\n"); goto free_port; } - mgmt_get_port_name(&phba->ctrl, phba); + beiscsi_get_port_name(&phba->ctrl, phba); beiscsi_get_params(phba); beiscsi_set_uer_feature(phba); diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 08d94b014459..706a817b8313 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -189,267 +189,6 @@ int beiscsi_modify_eq_delay(struct beiscsi_hba *phba, return tag; } -/** - * mgmt_get_port_name()- Get port name for the function - * @ctrl: ptr to Ctrl Info - * @phba: ptr to the dev priv structure - * - * Get the alphanumeric character for port - * - **/ -int mgmt_get_port_name(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba) -{ - int ret = 0; - struct be_mcc_wrb *wrb; - struct be_cmd_get_port_name *ioctl; - - mutex_lock(&ctrl->mbox_lock); - wrb = wrb_from_mbox(&ctrl->mbox_mem); - memset(wrb, 0, sizeof(*wrb)); - ioctl = embedded_payload(wrb); - - be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0); - be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_GET_PORT_NAME, - EMBED_MBX_MAX_PAYLOAD_SIZE); - ret = be_mbox_notify(ctrl); - phba->port_name = 0; - if (!ret) { - phba->port_name = ioctl->p.resp.port_names >> - (phba->fw_config.phys_port * 8) & 0xff; - } else { - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : GET_PORT_NAME ret 0x%x status 0x%x\n", - ret, ioctl->h.resp_hdr.status); - } - - if (phba->port_name == 0) - phba->port_name = '?'; - - mutex_unlock(&ctrl->mbox_lock); - return ret; -} - -/** - * mgmt_get_fw_config()- Get the FW config for the function - * @ctrl: ptr to Ctrl Info - * @phba: ptr to the dev priv structure - * - * Get the FW config and resources available for the function. - * The resources are created based on the count received here. - * - * return - * Success: 0 - * Failure: Non-Zero Value - **/ -int mgmt_get_fw_config(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba) -{ - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_fw_cfg *pfw_cfg = embedded_payload(wrb); - uint32_t cid_count, icd_count; - int status = -EINVAL; - uint8_t ulp_num = 0; - - mutex_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - be_wrb_hdr_prepare(wrb, sizeof(*pfw_cfg), true, 0); - - be_cmd_hdr_prepare(&pfw_cfg->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, - EMBED_MBX_MAX_PAYLOAD_SIZE); - - if (be_mbox_notify(ctrl)) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : Failed in mgmt_get_fw_config\n"); - goto fail_init; - } - - /* FW response formats depend on port id */ - phba->fw_config.phys_port = pfw_cfg->phys_port; - if (phba->fw_config.phys_port >= BEISCSI_PHYS_PORT_MAX) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : invalid physical port id %d\n", - phba->fw_config.phys_port); - goto fail_init; - } - - /* populate and check FW config against min and max values */ - if (!is_chip_be2_be3r(phba)) { - phba->fw_config.eqid_count = pfw_cfg->eqid_count; - phba->fw_config.cqid_count = pfw_cfg->cqid_count; - if (phba->fw_config.eqid_count == 0 || - phba->fw_config.eqid_count > 2048) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : invalid EQ count %d\n", - phba->fw_config.eqid_count); - goto fail_init; - } - if (phba->fw_config.cqid_count == 0 || - phba->fw_config.cqid_count > 4096) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : invalid CQ count %d\n", - phba->fw_config.cqid_count); - goto fail_init; - } - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : EQ_Count : %d CQ_Count : %d\n", - phba->fw_config.eqid_count, - phba->fw_config.cqid_count); - } - - /** - * Check on which all ULP iSCSI Protocol is loaded. - * Set the Bit for those ULP. This set flag is used - * at all places in the code to check on which ULP - * iSCSi Protocol is loaded - **/ - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { - if (pfw_cfg->ulp[ulp_num].ulp_mode & - BEISCSI_ULP_ISCSI_INI_MODE) { - set_bit(ulp_num, &phba->fw_config.ulp_supported); - - /* Get the CID, ICD and Chain count for each ULP */ - phba->fw_config.iscsi_cid_start[ulp_num] = - pfw_cfg->ulp[ulp_num].sq_base; - phba->fw_config.iscsi_cid_count[ulp_num] = - pfw_cfg->ulp[ulp_num].sq_count; - - phba->fw_config.iscsi_icd_start[ulp_num] = - pfw_cfg->ulp[ulp_num].icd_base; - phba->fw_config.iscsi_icd_count[ulp_num] = - pfw_cfg->ulp[ulp_num].icd_count; - - phba->fw_config.iscsi_chain_start[ulp_num] = - pfw_cfg->chain_icd[ulp_num].chain_base; - phba->fw_config.iscsi_chain_count[ulp_num] = - pfw_cfg->chain_icd[ulp_num].chain_count; - - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : Function loaded on ULP : %d\n" - "\tiscsi_cid_count : %d\n" - "\tiscsi_cid_start : %d\n" - "\t iscsi_icd_count : %d\n" - "\t iscsi_icd_start : %d\n", - ulp_num, - phba->fw_config. - iscsi_cid_count[ulp_num], - phba->fw_config. - iscsi_cid_start[ulp_num], - phba->fw_config. - iscsi_icd_count[ulp_num], - phba->fw_config. - iscsi_icd_start[ulp_num]); - } - } - - if (phba->fw_config.ulp_supported == 0) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : iSCSI initiator mode not set: ULP0 %x ULP1 %x\n", - pfw_cfg->ulp[BEISCSI_ULP0].ulp_mode, - pfw_cfg->ulp[BEISCSI_ULP1].ulp_mode); - goto fail_init; - } - - /** - * ICD is shared among ULPs. Use icd_count of any one loaded ULP - **/ - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) - if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) - break; - icd_count = phba->fw_config.iscsi_icd_count[ulp_num]; - if (icd_count == 0 || icd_count > 65536) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d: invalid ICD count %d\n", icd_count); - goto fail_init; - } - - cid_count = BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP0) + - BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP1); - if (cid_count == 0 || cid_count > 4096) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d: invalid CID count %d\n", cid_count); - goto fail_init; - } - - /** - * Check FW is dual ULP aware i.e. can handle either - * of the protocols. - */ - phba->fw_config.dual_ulp_aware = (pfw_cfg->function_mode & - BEISCSI_FUNC_DUA_MODE); - - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : DUA Mode : 0x%x\n", - phba->fw_config.dual_ulp_aware); - - /* all set, continue using this FW config */ - status = 0; -fail_init: - mutex_unlock(&ctrl->mbox_lock); - return status; -} - -int mgmt_check_supported_fw(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba) -{ - struct be_dma_mem nonemb_cmd; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_mgmt_controller_attributes *req; - struct be_sge *sge = nonembedded_sgl(wrb); - int status = 0; - - nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev, - sizeof(struct be_mgmt_controller_attributes), - &nonemb_cmd.dma); - if (nonemb_cmd.va == NULL) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : Failed to allocate memory for " - "mgmt_check_supported_fw\n"); - return -ENOMEM; - } - nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes); - req = nonemb_cmd.va; - memset(req, 0, sizeof(*req)); - mutex_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req)); - sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); - sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); - sge->len = cpu_to_le32(nonemb_cmd.size); - status = be_mbox_notify(ctrl); - if (!status) { - struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va; - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : Firmware Version of CMD : %s\n" - "Firmware Version is : %s\n" - "Developer Build, not performing version check...\n", - resp->params.hba_attribs - .flashrom_version_string, - resp->params.hba_attribs. - firmware_version_string); - - phba->fw_config.iscsi_features = - resp->params.hba_attribs.iscsi_features; - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BM_%d : phba->fw_config.iscsi_features = %d\n", - phba->fw_config.iscsi_features); - memcpy(phba->fw_ver_str, resp->params.hba_attribs. - firmware_version_string, BEISCSI_VER_STRLEN); - } else - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : Failed in mgmt_check_supported_fw\n"); - mutex_unlock(&ctrl->mbox_lock); - if (nonemb_cmd.va) - pci_free_consistent(ctrl->pdev, nonemb_cmd.size, - nonemb_cmd.va, nonemb_cmd.dma); - - return status; -} - unsigned int mgmt_vendor_specific_fw_cmd(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba, struct bsg_job *job, diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index 25053483cf28..f8d27783f207 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -266,11 +266,6 @@ struct beiscsi_endpoint { u16 cid_vld; }; -int mgmt_get_fw_config(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba); -int mgmt_get_port_name(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba); - unsigned int mgmt_invalidate_connection(struct beiscsi_hba *phba, struct beiscsi_endpoint *beiscsi_ep, unsigned short cid, @@ -282,17 +277,17 @@ int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type); int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, u8 *ip, u8 *subnet); +int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw); + +int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, + struct be_cmd_get_def_gateway_resp *resp); + int mgmt_get_nic_conf(struct beiscsi_hba *phba, struct be_cmd_get_nic_conf_resp *mac); int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type, struct be_cmd_get_if_info_resp **if_info); -int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, - struct be_cmd_get_def_gateway_resp *resp); - -int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw); - unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba); int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); From 4d2ee1e688a26ee580b9f3e824afa278bbb178a5 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:16 +0530 Subject: [PATCH 047/173] scsi: be2iscsi: Fix POST check and reset sequence SLIPORT FUNCTION_RESET does not reset the chip. So POST status needs to be checked before issuing FUNCTION_RESET. The completion of FUNCTION_RESET is indicated in BMBX Rdy bit. be_cmd_fw_initialize too needs to be done before issuing any cmd to FW. be_cmd_fw_initialize is renamed as beiscsi_cmd_special_wrb. Rearrange and rename few functions in init and cleanup path. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 250 ++++++++++++++++---------------- drivers/scsi/be2iscsi/be_cmds.h | 38 +++-- drivers/scsi/be2iscsi/be_main.c | 66 +++------ drivers/scsi/be2iscsi/be_main.h | 8 - 4 files changed, 165 insertions(+), 197 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 1ebb6ce12742..771670c0682b 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -21,35 +21,6 @@ #include "be.h" #include "be_mgmt.h" -int be_chk_reset_complete(struct beiscsi_hba *phba) -{ - unsigned int num_loop; - u8 *mpu_sem = 0; - u32 status; - - num_loop = 1000; - mpu_sem = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE; - msleep(5000); - - while (num_loop) { - status = readl((void *)mpu_sem); - - if ((status & 0x80000000) || (status & 0x0000FFFF) == 0xC000) - break; - msleep(60); - num_loop--; - } - - if ((status & 0x80000000) || (!num_loop)) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BC_%d : Failed in be_chk_reset_complete" - "status = 0x%x\n", status); - return -EIO; - } - - return 0; -} - struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, unsigned int *ref_tag) { @@ -769,87 +740,6 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, return status; } -/** - * be_cmd_fw_initialize()- Initialize FW - * @ctrl: Pointer to function control structure - * - * Send FW initialize pattern for the function. - * - * return - * Success: 0 - * Failure: Non-Zero value - **/ -int be_cmd_fw_initialize(struct be_ctrl_info *ctrl) -{ - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev); - int status; - u8 *endian_check; - - mutex_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - - endian_check = (u8 *) wrb; - *endian_check++ = 0xFF; - *endian_check++ = 0x12; - *endian_check++ = 0x34; - *endian_check++ = 0xFF; - *endian_check++ = 0xFF; - *endian_check++ = 0x56; - *endian_check++ = 0x78; - *endian_check++ = 0xFF; - be_dws_cpu_to_le(wrb, sizeof(*wrb)); - - status = be_mbox_notify(ctrl); - if (status) - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BC_%d : be_cmd_fw_initialize Failed\n"); - - mutex_unlock(&ctrl->mbox_lock); - return status; -} - -/** - * be_cmd_fw_uninit()- Uinitialize FW - * @ctrl: Pointer to function control structure - * - * Send FW uninitialize pattern for the function - * - * return - * Success: 0 - * Failure: Non-Zero value - **/ -int be_cmd_fw_uninit(struct be_ctrl_info *ctrl) -{ - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev); - int status; - u8 *endian_check; - - mutex_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - - endian_check = (u8 *) wrb; - *endian_check++ = 0xFF; - *endian_check++ = 0xAA; - *endian_check++ = 0xBB; - *endian_check++ = 0xFF; - *endian_check++ = 0xFF; - *endian_check++ = 0xCC; - *endian_check++ = 0xDD; - *endian_check = 0xFF; - - be_dws_cpu_to_le(wrb, sizeof(*wrb)); - - status = be_mbox_notify(ctrl); - if (status) - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BC_%d : be_cmd_fw_uninit Failed\n"); - - mutex_unlock(&ctrl->mbox_lock); - return status; -} - int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, struct be_queue_info *cq, struct be_queue_info *eq, bool sol_evts, bool no_delay, int coalesce_wm) @@ -1293,25 +1183,6 @@ error: return status; } -int beiscsi_cmd_reset_function(struct beiscsi_hba *phba) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_post_sgl_pages_req *req = embedded_payload(wrb); - int status; - - mutex_lock(&ctrl->mbox_lock); - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_FUNCTION_RESET, sizeof(*req)); - status = be_mbox_notify(ctrl); - - mutex_unlock(&ctrl->mbox_lock); - return status; -} - /** * be_cmd_set_vlan()- Configure VLAN paramters on the adapter * @phba: device priv structure instance @@ -1653,3 +1524,124 @@ int beiscsi_set_uer_feature(struct beiscsi_hba *phba) mutex_unlock(&ctrl->mbox_lock); return ret; } + +static u32 beiscsi_get_post_stage(struct beiscsi_hba *phba) +{ + u32 sem; + + if (is_chip_be2_be3r(phba)) + sem = ioread32(phba->csr_va + SLIPORT_SEMAPHORE_OFFSET_BEx); + else + pci_read_config_dword(phba->pcidev, + SLIPORT_SEMAPHORE_OFFSET_SH, &sem); + return sem; +} + +int beiscsi_check_fw_rdy(struct beiscsi_hba *phba) +{ + u32 loop, post, rdy = 0; + + loop = 1000; + while (loop--) { + post = beiscsi_get_post_stage(phba); + if (post & POST_ERROR_BIT) + break; + if ((post & POST_STAGE_MASK) == POST_STAGE_ARMFW_RDY) { + rdy = 1; + break; + } + msleep(60); + } + + if (!rdy) { + __beiscsi_log(phba, KERN_ERR, + "BC_%d : FW not ready 0x%x\n", post); + } + + return rdy; +} + +static int beiscsi_cmd_function_reset(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_post_sgl_pages_req *req = embedded_payload(wrb); + int status; + + mutex_lock(&ctrl->mbox_lock); + + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_FUNCTION_RESET, sizeof(*req)); + status = be_mbox_notify(ctrl); + + mutex_unlock(&ctrl->mbox_lock); + return status; +} + +int beiscsi_cmd_special_wrb(struct be_ctrl_info *ctrl, u32 load) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev); + u8 *endian_check; + int status; + + mutex_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + endian_check = (u8 *) wrb; + if (load) { + /* to start communicating */ + *endian_check++ = 0xFF; + *endian_check++ = 0x12; + *endian_check++ = 0x34; + *endian_check++ = 0xFF; + *endian_check++ = 0xFF; + *endian_check++ = 0x56; + *endian_check++ = 0x78; + *endian_check++ = 0xFF; + } else { + /* to stop communicating */ + *endian_check++ = 0xFF; + *endian_check++ = 0xAA; + *endian_check++ = 0xBB; + *endian_check++ = 0xFF; + *endian_check++ = 0xFF; + *endian_check++ = 0xCC; + *endian_check++ = 0xDD; + *endian_check = 0xFF; + } + be_dws_cpu_to_le(wrb, sizeof(*wrb)); + + status = be_mbox_notify(ctrl); + if (status) + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BC_%d : special WRB message failed\n"); + mutex_unlock(&ctrl->mbox_lock); + return status; +} + +int beiscsi_init_sliport(struct beiscsi_hba *phba) +{ + int status; + + /* check POST stage before talking to FW */ + status = beiscsi_check_fw_rdy(phba); + if (!status) + return -EIO; + + /* + * SLI COMMON_FUNCTION_RESET completion is indicated by BMBX RDY bit. + * It should clean up any stale info in FW for this fn. + */ + status = beiscsi_cmd_function_reset(phba); + if (status) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BC_%d : SLI Function Reset failed\n"); + return status; + } + + /* indicate driver is loading */ + return beiscsi_cmd_special_wrb(&phba->ctrl, 1); +} diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index c9823b00b254..47b7197f17a2 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -98,11 +98,23 @@ struct be_mcc_compl { #define MPU_MAILBOX_DB_RDY_MASK 0x1 /* bit 0 */ #define MPU_MAILBOX_DB_HI_MASK 0x2 /* bit 1 */ -/********** MPU semphore ******************/ -#define MPU_EP_SEMAPHORE_OFFSET 0xac -#define EP_SEMAPHORE_POST_STAGE_MASK 0x0000FFFF -#define EP_SEMAPHORE_POST_ERR_MASK 0x1 -#define EP_SEMAPHORE_POST_ERR_SHIFT 31 +/********** MPU semphore: used for SH & BE ******************/ +#define SLIPORT_SOFTRESET_OFFSET 0x5c /* CSR BAR offset */ +#define SLIPORT_SEMAPHORE_OFFSET_BEx 0xac /* CSR BAR offset */ +#define SLIPORT_SEMAPHORE_OFFSET_SH 0x94 /* PCI-CFG offset */ +#define POST_STAGE_MASK 0x0000FFFF +#define POST_ERROR_BIT 0x80000000 +#define POST_ERR_RECOVERY_CODE_MASK 0xF000 + +/* Soft Reset register masks */ +#define SLIPORT_SOFTRESET_SR_MASK 0x00000080 /* SR bit */ + +/* MPU semphore POST stage values */ +#define POST_STAGE_AWAITING_HOST_RDY 0x1 /* FW awaiting goahead from host */ +#define POST_STAGE_HOST_RDY 0x2 /* Host has given go-ahed to FW */ +#define POST_STAGE_BE_RESET 0x3 /* Host wants to reset chip */ +#define POST_STAGE_ARMFW_RDY 0xC000 /* FW is done with POST */ +#define POST_STAGE_RECOVERABLE_ERR 0xE000 /* Recoverable err detected */ /********** MCC door bell ************/ #define DB_MCCQ_OFFSET 0x140 @@ -110,9 +122,6 @@ struct be_mcc_compl { /* Number of entries posted */ #define DB_MCCQ_NUM_POSTED_SHIFT 16 /* bits 16 - 29 */ -/* MPU semphore POST stage values */ -#define POST_STAGE_ARMFW_RDY 0xc000 /* FW is done with POST */ - /** * When the async bit of mcc_compl is set, the last 4 bytes of * mcc_compl is interpreted as follows: @@ -753,6 +762,12 @@ struct be_cmd_set_features { } param; } __packed; +int beiscsi_cmd_special_wrb(struct be_ctrl_info *ctrl, u32 load); + +int beiscsi_check_fw_rdy(struct beiscsi_hba *phba); + +int beiscsi_init_sliport(struct beiscsi_hba *phba); + int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_queue_info *eq, int eq_delay); @@ -784,9 +799,6 @@ int __beiscsi_mcc_compl_status(struct beiscsi_hba *phba, struct be_mcc_wrb **wrb, struct be_dma_mem *mbx_cmd_mem); /*ISCSI Functuions */ -int be_cmd_fw_initialize(struct be_ctrl_info *ctrl); -int be_cmd_fw_uninit(struct be_ctrl_info *ctrl); - struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem); int be_mcc_compl_poll(struct beiscsi_hba *phba, unsigned int tag); void be_mcc_notify(struct beiscsi_hba *phba, unsigned int tag); @@ -812,8 +824,6 @@ int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, u32 page_offset, u32 num_pages); -int beiscsi_cmd_reset_function(struct beiscsi_hba *phba); - int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, struct be_queue_info *wrbq, struct hwi_wrb_context *pwrb_context, @@ -1422,8 +1432,6 @@ struct be_cmd_get_port_name { * the cxn */ -int be_chk_reset_complete(struct beiscsi_hba *phba); - void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, bool embedded, u8 sge_cnt); diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 77ecd6c48207..b4e6fc222d60 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -3544,7 +3544,7 @@ static void be_mcc_queues_destroy(struct beiscsi_hba *phba) } } -static void hwi_cleanup(struct beiscsi_hba *phba) +static void hwi_cleanup_port(struct beiscsi_hba *phba) { struct be_queue_info *q; struct be_ctrl_info *ctrl = &phba->ctrl; @@ -3603,7 +3603,8 @@ static void hwi_cleanup(struct beiscsi_hba *phba) beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); } } - be_cmd_fw_uninit(ctrl); + /* last communication, indicate driver is unloading */ + beiscsi_cmd_special_wrb(&phba->ctrl, 0); } static int be_mcc_queues_create(struct beiscsi_hba *phba, @@ -3700,8 +3701,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) phwi_context->max_eqd = 128; phwi_context->min_eqd = 0; phwi_context->cur_eqd = 0; - be_cmd_fw_initialize(&phba->ctrl); - /* set optic state to unknown */ + /* set port optic state to unknown */ phba->optic_state = 0xff; status = beiscsi_create_eqs(phba, phwi_context); @@ -3807,7 +3807,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) error: beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : hwi_init_port failed"); - hwi_cleanup(phba); + hwi_cleanup_port(phba); return status; } @@ -4196,7 +4196,7 @@ static int beiscsi_init_port(struct beiscsi_hba *phba) return ret; do_cleanup_ctrlr: - hwi_cleanup(phba); + hwi_cleanup_port(phba); return ret; } @@ -4233,7 +4233,7 @@ static void hwi_purge_eq(struct beiscsi_hba *phba) } } -static void beiscsi_clean_port(struct beiscsi_hba *phba) +static void beiscsi_cleanup_port(struct beiscsi_hba *phba) { int mgmt_status, ulp_num; struct ulp_cid_info *ptr_cid_info = NULL; @@ -4250,7 +4250,7 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba) } hwi_purge_eq(phba); - hwi_cleanup(phba); + hwi_cleanup_port(phba); kfree(phba->io_sgl_hndl_base); kfree(phba->eh_sgl_hndl_base); kfree(phba->ep_array); @@ -5011,12 +5011,12 @@ static void beiscsi_quiesce(struct beiscsi_hba *phba) /* PCI_ERR is set then check if driver is not unloading */ if (test_bit(BEISCSI_HBA_RUNNING, &phba->state) && test_bit(BEISCSI_HBA_PCI_ERR, &phba->state)) { - hwi_cleanup(phba); + hwi_cleanup_port(phba); return; } destroy_workqueue(phba->wq); - beiscsi_clean_port(phba); + beiscsi_cleanup_port(phba); beiscsi_free_mem(phba); beiscsi_unmap_pci_function(phba); @@ -5461,9 +5461,8 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - /* Wait for the CHIP Reset to complete */ - status = be_chk_reset_complete(phba); - if (!status) { + status = beiscsi_check_fw_rdy(phba); + if (status) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT, "BM_%d : EEH Reset Completed\n"); } else { @@ -5478,7 +5477,7 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev) static void beiscsi_eeh_resume(struct pci_dev *pdev) { - int ret = 0, i; + int ret, i; struct be_eq_obj *pbe_eq; struct beiscsi_hba *phba = NULL; struct hwi_controller *phwi_ctrlr; @@ -5498,19 +5497,9 @@ static void beiscsi_eeh_resume(struct pci_dev *pdev) phba->num_cpus = 1; } - ret = beiscsi_cmd_reset_function(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Reset Failed\n"); + ret = beiscsi_init_sliport(phba); + if (ret) goto ret_err; - } - - ret = be_chk_reset_complete(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Failed to get out of reset.\n"); - goto ret_err; - } beiscsi_get_params(phba); phba->shost->max_id = phba->params.cxns_per_ctrl; @@ -5627,28 +5616,15 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, ret = be_ctrl_init(phba, pcidev); if (ret) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : beiscsi_dev_probe-" - "Failed in be_ctrl_init\n"); + "BM_%d : be_ctrl_init failed\n"); goto hba_free; } + ret = beiscsi_init_sliport(phba); + if (ret) + goto hba_free; + set_bit(BEISCSI_HBA_RUNNING, &phba->state); - /* - * FUNCTION_RESET should clean up any stale info in FW for this fn - */ - ret = beiscsi_cmd_reset_function(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Reset Failed\n"); - goto hba_free; - } - ret = be_chk_reset_complete(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Failed to get out of reset.\n"); - goto hba_free; - } - spin_lock_init(&phba->io_sgl_lock); spin_lock_init(&phba->mgmt_sgl_lock); spin_lock_init(&phba->async_pdu_lock); @@ -5772,7 +5748,7 @@ free_blkenbld: irq_poll_disable(&pbe_eq->iopoll); } free_twq: - beiscsi_clean_port(phba); + beiscsi_cleanup_port(phba); beiscsi_free_mem(phba); free_port: pci_free_consistent(phba->pcidev, diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 0a5de01c3883..4cdb34c3de5d 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -82,14 +82,6 @@ #define BEISCSI_MAX_FRAGS_INIT 192 #define BE_NUM_MSIX_ENTRIES 1 -#define MPU_EP_CONTROL 0 -#define MPU_EP_SEMAPHORE 0xac -#define BE2_SOFT_RESET 0x5c -#define BE2_PCI_ONLINE0 0xb0 -#define BE2_PCI_ONLINE1 0xb4 -#define BE2_SET_RESET 0x80 -#define BE2_MPU_IRAM_ONLINE 0x00000080 - #define BE_SENSE_INFO_SIZE 258 #define BE_ISCSI_PDU_HEADER_SIZE 64 #define BE_MIN_MEM_SIZE 16384 From f79929deb56e1b8053c36adf7ee8d34b39e673a8 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:17 +0530 Subject: [PATCH 048/173] scsi: be2iscsi: Add V1 of EPFW cleanup IOCTL mgmt_epfw_cleanup does not implement v1 of OPCODE_COMMON_ISCSI_CLEANUP IOCTL for SkyHawk. Replace use of MCCQ with BMBX for issuing the IOCTL. Remove be_mcc_compl_poll which is no longer needed. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 96 +++++++++++++++++---------------- drivers/scsi/be2iscsi/be_cmds.h | 15 ++++-- drivers/scsi/be2iscsi/be_main.c | 27 +++++----- drivers/scsi/be2iscsi/be_mgmt.c | 42 --------------- drivers/scsi/be2iscsi/be_mgmt.h | 1 - 5 files changed, 73 insertions(+), 108 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 771670c0682b..6f3cd82c4295 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -478,53 +478,6 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, return 0; } -/* - * be_mcc_compl_poll()- Wait for MBX completion - * @phba: driver private structure - * - * Wait till no more pending mcc requests are present - * - * return - * Success: 0 - * Failure: Non-Zero - * - **/ -int be_mcc_compl_poll(struct beiscsi_hba *phba, unsigned int tag) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - int i; - - if (!test_bit(MCC_TAG_STATE_RUNNING, - &ctrl->ptag_state[tag].tag_state)) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BC_%d: tag %u state not running\n", tag); - return 0; - } - for (i = 0; i < mcc_timeout; i++) { - if (beiscsi_hba_in_error(phba)) - return -EIO; - - beiscsi_process_mcc_cq(phba); - /* after polling, wrb and tag need to be released */ - if (!test_bit(MCC_TAG_STATE_RUNNING, - &ctrl->ptag_state[tag].tag_state)) { - free_mcc_wrb(ctrl, tag); - break; - } - udelay(100); - } - - if (i < mcc_timeout) - return 0; - - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BC_%d : FW Timed Out\n"); - set_bit(BEISCSI_HBA_FW_TIMEOUT, &phba->state); - beiscsi_ue_detect(phba); - return -EBUSY; -} - void be_mcc_notify(struct beiscsi_hba *phba, unsigned int tag) { struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; @@ -1645,3 +1598,52 @@ int beiscsi_init_sliport(struct beiscsi_hba *phba) /* indicate driver is loading */ return beiscsi_cmd_special_wrb(&phba->ctrl, 1); } + +/** + * beiscsi_cmd_iscsi_cleanup()- Inform FW to cleanup EP data structures. + * @phba: pointer to dev priv structure + * @ulp: ULP number. + * + * return + * Success: 0 + * Failure: Non-Zero Value + **/ +int beiscsi_cmd_iscsi_cleanup(struct beiscsi_hba *phba, unsigned short ulp) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct iscsi_cleanup_req_v1 *req_v1; + struct iscsi_cleanup_req *req; + struct be_mcc_wrb *wrb; + int status; + + mutex_lock(&ctrl->mbox_lock); + wrb = wrb_from_mbox(&ctrl->mbox_mem); + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req)); + + /** + * TODO: Check with FW folks the chute value to be set. + * For now, use the ULP_MASK as the chute value. + */ + if (is_chip_be2_be3r(phba)) { + req->chute = (1 << ulp); + req->hdr_ring_id = HWI_GET_DEF_HDRQ_ID(phba, ulp); + req->data_ring_id = HWI_GET_DEF_BUFQ_ID(phba, ulp); + } else { + req_v1 = (struct iscsi_cleanup_req_v1 *)req; + req_v1->hdr.version = 1; + req_v1->hdr_ring_id = cpu_to_le16(HWI_GET_DEF_HDRQ_ID(phba, + ulp)); + req_v1->data_ring_id = cpu_to_le16(HWI_GET_DEF_BUFQ_ID(phba, + ulp)); + } + + status = be_mbox_notify(ctrl); + if (status) + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT, + "BG_%d : %s failed %d\n", __func__, ulp); + mutex_unlock(&ctrl->mbox_lock); + return status; +} diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index 47b7197f17a2..cf4239987acf 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -768,6 +768,8 @@ int beiscsi_check_fw_rdy(struct beiscsi_hba *phba); int beiscsi_init_sliport(struct beiscsi_hba *phba); +int beiscsi_cmd_iscsi_cleanup(struct beiscsi_hba *phba, unsigned short ulp_num); + int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_queue_info *eq, int eq_delay); @@ -782,8 +784,6 @@ int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba, struct be_queue_info *mccq, struct be_queue_info *cq); -int be_poll_mcc(struct be_ctrl_info *ctrl); - unsigned int be_cmd_get_initname(struct beiscsi_hba *phba); void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag); @@ -798,9 +798,7 @@ int __beiscsi_mcc_compl_status(struct beiscsi_hba *phba, unsigned int tag, struct be_mcc_wrb **wrb, struct be_dma_mem *mbx_cmd_mem); -/*ISCSI Functuions */ struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem); -int be_mcc_compl_poll(struct beiscsi_hba *phba, unsigned int tag); void be_mcc_notify(struct beiscsi_hba *phba, unsigned int tag); struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, unsigned int *ref_tag); @@ -1063,7 +1061,16 @@ struct iscsi_cleanup_req { u16 chute; u8 hdr_ring_id; u8 data_ring_id; +} __packed; +struct iscsi_cleanup_req_v1 { + struct be_cmd_req_hdr hdr; + u16 chute; + u16 rsvd1; + u16 hdr_ring_id; + u16 rsvd2; + u16 data_ring_id; + u16 rsvd3; } __packed; struct eq_delay { diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index b4e6fc222d60..e7741072995a 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -3249,7 +3249,6 @@ beiscsi_create_def_hdr(struct beiscsi_hba *phba, "BM_%d : iscsi hdr def pdu id for ULP : %d is %d\n", ulp_num, phwi_context->be_def_hdrq[ulp_num].id); - hwi_post_async_buffers(phba, BEISCSI_DEFQ_HDR, ulp_num); return 0; } @@ -3304,11 +3303,9 @@ beiscsi_create_def_data(struct beiscsi_hba *phba, ulp_num, phwi_context->be_def_dataq[ulp_num].id); - hwi_post_async_buffers(phba, BEISCSI_DEFQ_DATA, ulp_num); beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, "BM_%d : DEFAULT PDU DATA RING CREATED" "on ULP : %d\n", ulp_num); - return 0; } @@ -3757,6 +3754,15 @@ static int hwi_init_port(struct beiscsi_hba *phba) ulp_num); goto error; } + /** + * Now that the default PDU rings have been created, + * let EP know about it. + * Call beiscsi_cmd_iscsi_cleanup before posting? + */ + hwi_post_async_buffers(phba, BEISCSI_DEFQ_HDR, + ulp_num); + hwi_post_async_buffers(phba, BEISCSI_DEFQ_DATA, + ulp_num); } } @@ -4235,19 +4241,12 @@ static void hwi_purge_eq(struct beiscsi_hba *phba) static void beiscsi_cleanup_port(struct beiscsi_hba *phba) { - int mgmt_status, ulp_num; struct ulp_cid_info *ptr_cid_info = NULL; + int ulp_num; - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { - if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) { - mgmt_status = mgmt_epfw_cleanup(phba, ulp_num); - if (mgmt_status) - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_INIT, - "BM_%d : mgmt_epfw_cleanup FAILED" - " for ULP_%d\n", ulp_num); - } - } + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) + if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) + beiscsi_cmd_iscsi_cleanup(phba, ulp_num); hwi_purge_eq(phba); hwi_cleanup_port(phba); diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 706a817b8313..a844299ea979 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -256,48 +256,6 @@ unsigned int mgmt_vendor_specific_fw_cmd(struct be_ctrl_info *ctrl, return tag; } -/** - * mgmt_epfw_cleanup()- Inform FW to cleanup data structures. - * @phba: pointer to dev priv structure - * @ulp_num: ULP number. - * - * return - * Success: 0 - * Failure: Non-Zero Value - **/ -int mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short ulp_num) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct iscsi_cleanup_req *req; - unsigned int tag; - int status; - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return -EBUSY; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, - OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req)); - - req->chute = (1 << ulp_num); - req->hdr_ring_id = cpu_to_le16(HWI_GET_DEF_HDRQ_ID(phba, ulp_num)); - req->data_ring_id = cpu_to_le16(HWI_GET_DEF_BUFQ_ID(phba, ulp_num)); - - be_mcc_notify(phba, tag); - status = be_mcc_compl_poll(phba, tag); - if (status) - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT, - "BG_%d : mgmt_epfw_cleanup , FAILED\n"); - mutex_unlock(&ctrl->mbox_lock); - return status; -} - unsigned int mgmt_invalidate_icds(struct beiscsi_hba *phba, struct invalidate_command_table *inv_tbl, unsigned int num_invalidate, unsigned int cid, diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index f8d27783f207..dab128fb2ce3 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -96,7 +96,6 @@ struct mcc_wrb { struct mcc_wrb_payload payload; }; -int mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute); int mgmt_open_connection(struct beiscsi_hba *phba, struct sockaddr *dst_addr, struct beiscsi_endpoint *beiscsi_ep, From d1d5ca887c0ee60ec6c6e42db0c1073155a09d32 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:18 +0530 Subject: [PATCH 049/173] scsi: be2iscsi: Add TPE recovery feature After UE is detected, check for recoverable error by reading SLIPORT SEMAPHORE register. If transient parity error i.e. 0xExxx then schedule recovery work on driver wq. FLag this error to prevent any transactions for the duration of ue2rp to restart polling. After that, if FW becomes ready then recover port. Wake up processes in wq before going offline. Wait for process to execute before cleaning up. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 173 +++++++- drivers/scsi/be2iscsi/be_cmds.h | 4 + drivers/scsi/be2iscsi/be_iscsi.c | 14 +- drivers/scsi/be2iscsi/be_main.c | 733 +++++++++++++++++++------------ drivers/scsi/be2iscsi/be_main.h | 6 +- drivers/scsi/be2iscsi/be_mgmt.c | 128 ------ drivers/scsi/be2iscsi/be_mgmt.h | 1 - 7 files changed, 629 insertions(+), 430 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 6f3cd82c4295..ad7405d6821f 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -21,6 +21,78 @@ #include "be.h" #include "be_mgmt.h" +/* UE Status Low CSR */ +static const char * const desc_ue_status_low[] = { + "CEV", + "CTX", + "DBUF", + "ERX", + "Host", + "MPU", + "NDMA", + "PTC ", + "RDMA ", + "RXF ", + "RXIPS ", + "RXULP0 ", + "RXULP1 ", + "RXULP2 ", + "TIM ", + "TPOST ", + "TPRE ", + "TXIPS ", + "TXULP0 ", + "TXULP1 ", + "UC ", + "WDMA ", + "TXULP2 ", + "HOST1 ", + "P0_OB_LINK ", + "P1_OB_LINK ", + "HOST_GPIO ", + "MBOX ", + "AXGMAC0", + "AXGMAC1", + "JTAG", + "MPU_INTPEND" +}; + +/* UE Status High CSR */ +static const char * const desc_ue_status_hi[] = { + "LPCMEMHOST", + "MGMT_MAC", + "PCS0ONLINE", + "MPU_IRAM", + "PCS1ONLINE", + "PCTL0", + "PCTL1", + "PMEM", + "RR", + "TXPB", + "RXPP", + "XAUI", + "TXP", + "ARM", + "IPC", + "HOST2", + "HOST3", + "HOST4", + "HOST5", + "HOST6", + "HOST7", + "HOST8", + "HOST9", + "NETC", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown" +}; + struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, unsigned int *ref_tag) { @@ -185,6 +257,16 @@ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, phba->ctrl.mcc_tag_status[tag], msecs_to_jiffies( BEISCSI_HOST_MBX_TIMEOUT)); + /** + * Return EIO if port is being disabled. Associated DMA memory, if any, + * is freed by the caller. When port goes offline, MCCQ is cleaned up + * so does WRB. + */ + if (!test_bit(BEISCSI_HBA_ONLINE, &phba->state)) { + clear_bit(MCC_TAG_STATE_RUNNING, + &phba->ctrl.ptag_state[tag].tag_state); + return -EIO; + } /** * If MBOX cmd timeout expired, tag and resource allocated @@ -538,7 +620,6 @@ static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl) BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, "BC_%d : FW Timed Out\n"); set_bit(BEISCSI_HBA_FW_TIMEOUT, &phba->state); - beiscsi_ue_detect(phba); return -EBUSY; } @@ -1584,6 +1665,12 @@ int beiscsi_init_sliport(struct beiscsi_hba *phba) if (!status) return -EIO; + /* clear all error states after checking FW rdy */ + phba->state &= ~BEISCSI_HBA_IN_ERR; + + /* check again UER support */ + phba->state &= ~BEISCSI_HBA_UER_SUPP; + /* * SLI COMMON_FUNCTION_RESET completion is indicated by BMBX RDY bit. * It should clean up any stale info in FW for this fn. @@ -1647,3 +1734,87 @@ int beiscsi_cmd_iscsi_cleanup(struct beiscsi_hba *phba, unsigned short ulp) mutex_unlock(&ctrl->mbox_lock); return status; } + +/* + * beiscsi_detect_ue()- Detect Unrecoverable Error on adapter + * @phba: Driver priv structure + * + * Read registers linked to UE and check for the UE status + **/ +int beiscsi_detect_ue(struct beiscsi_hba *phba) +{ + uint32_t ue_mask_hi = 0, ue_mask_lo = 0; + uint32_t ue_hi = 0, ue_lo = 0; + uint8_t i = 0; + int ret = 0; + + pci_read_config_dword(phba->pcidev, + PCICFG_UE_STATUS_LOW, &ue_lo); + pci_read_config_dword(phba->pcidev, + PCICFG_UE_STATUS_MASK_LOW, + &ue_mask_lo); + pci_read_config_dword(phba->pcidev, + PCICFG_UE_STATUS_HIGH, + &ue_hi); + pci_read_config_dword(phba->pcidev, + PCICFG_UE_STATUS_MASK_HI, + &ue_mask_hi); + + ue_lo = (ue_lo & ~ue_mask_lo); + ue_hi = (ue_hi & ~ue_mask_hi); + + + if (ue_lo || ue_hi) { + set_bit(BEISCSI_HBA_IN_UE, &phba->state); + __beiscsi_log(phba, KERN_ERR, + "BC_%d : HBA error detected\n"); + ret = 1; + } + + if (ue_lo) { + for (i = 0; ue_lo; ue_lo >>= 1, i++) { + if (ue_lo & 1) + __beiscsi_log(phba, KERN_ERR, + "BC_%d : UE_LOW %s bit set\n", + desc_ue_status_low[i]); + } + } + + if (ue_hi) { + for (i = 0; ue_hi; ue_hi >>= 1, i++) { + if (ue_hi & 1) + __beiscsi_log(phba, KERN_ERR, + "BC_%d : UE_HIGH %s bit set\n", + desc_ue_status_hi[i]); + } + } + return ret; +} + +/* + * beiscsi_detect_tpe()- Detect Transient Parity Error on adapter + * @phba: Driver priv structure + * + * Read SLIPORT SEMAPHORE register to check for UER + * + **/ +int beiscsi_detect_tpe(struct beiscsi_hba *phba) +{ + u32 post, status; + int ret = 0; + + post = beiscsi_get_post_stage(phba); + status = post & POST_STAGE_MASK; + if ((status & POST_ERR_RECOVERY_CODE_MASK) == + POST_STAGE_RECOVERABLE_ERR) { + set_bit(BEISCSI_HBA_IN_TPE, &phba->state); + __beiscsi_log(phba, KERN_INFO, + "BC_%d : HBA error recoverable: 0x%x\n", post); + ret = 1; + } else { + __beiscsi_log(phba, KERN_INFO, + "BC_%d : HBA in UE: 0x%x\n", post); + } + + return ret; +} diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index cf4239987acf..26d7921f48a4 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -770,6 +770,10 @@ int beiscsi_init_sliport(struct beiscsi_hba *phba); int beiscsi_cmd_iscsi_cleanup(struct beiscsi_hba *phba, unsigned short ulp_num); +int beiscsi_detect_ue(struct beiscsi_hba *phba); + +int beiscsi_detect_tpe(struct beiscsi_hba *phba); + int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_queue_info *eq, int eq_delay); diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 677491a4b0b4..35f7d3aab366 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -58,7 +58,7 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, beiscsi_ep = ep->dd_data; phba = beiscsi_ep->phba; - if (beiscsi_hba_in_error(phba)) { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); return NULL; @@ -444,7 +444,7 @@ int beiscsi_iface_set_param(struct Scsi_Host *shost, uint32_t rm_len = dt_len; int ret; - if (beiscsi_hba_in_error(phba)) { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; @@ -587,7 +587,7 @@ int beiscsi_iface_get_param(struct iscsi_iface *iface, if (param_type != ISCSI_NET_PARAM) return 0; - if (beiscsi_hba_in_error(phba)) { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; @@ -797,7 +797,7 @@ int beiscsi_get_host_param(struct Scsi_Host *shost, struct beiscsi_hba *phba = iscsi_host_priv(shost); int status = 0; - if (beiscsi_hba_in_error(phba)) { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; @@ -945,7 +945,7 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn) phba = ((struct beiscsi_conn *)conn->dd_data)->phba; - if (beiscsi_hba_in_error(phba)) { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; @@ -1175,7 +1175,7 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, } phba = iscsi_host_priv(shost); - if (beiscsi_hba_in_error(phba)) { + if (!beiscsi_hba_is_online(phba)) { ret = -EIO; beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); @@ -1335,7 +1335,7 @@ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep) tcp_upload_flag = CONNECTION_UPLOAD_ABORT; } - if (beiscsi_hba_in_error(phba)) { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); goto free_ep; diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index e7741072995a..0625bd0c449f 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -3525,10 +3525,53 @@ static void free_wrb_handles(struct beiscsi_hba *phba) static void be_mcc_queues_destroy(struct beiscsi_hba *phba) { - struct be_queue_info *q; struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_dma_mem *ptag_mem; + struct be_queue_info *q; + int i, tag; q = &phba->ctrl.mcc_obj.q; + for (i = 0; i < MAX_MCC_CMD; i++) { + tag = i + 1; + if (!test_bit(MCC_TAG_STATE_RUNNING, + &ctrl->ptag_state[tag].tag_state)) + continue; + + if (test_bit(MCC_TAG_STATE_TIMEOUT, + &ctrl->ptag_state[tag].tag_state)) { + ptag_mem = &ctrl->ptag_state[tag].tag_mem_state; + if (ptag_mem->size) { + pci_free_consistent(ctrl->pdev, + ptag_mem->size, + ptag_mem->va, + ptag_mem->dma); + ptag_mem->size = 0; + } + continue; + } + /** + * If MCC is still active and waiting then wake up the process. + * We are here only because port is going offline. The process + * sees that (BEISCSI_HBA_ONLINE is cleared) and EIO error is + * returned for the operation and allocated memory cleaned up. + */ + if (waitqueue_active(&ctrl->mcc_wait[tag])) { + ctrl->mcc_tag_status[tag] = MCC_STATUS_FAILED; + ctrl->mcc_tag_status[tag] |= CQE_VALID_MASK; + wake_up_interruptible(&ctrl->mcc_wait[tag]); + /* + * Control tag info gets reinitialized in enable + * so wait for the process to clear running state. + */ + while (test_bit(MCC_TAG_STATE_RUNNING, + &ctrl->ptag_state[tag].tag_state)) + schedule_timeout_uninterruptible(HZ); + } + /** + * For MCC with tag_states MCC_TAG_STATE_ASYNC and + * MCC_TAG_STATE_IGNORE nothing needs to done. + */ + } if (q->created) { beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ); be_queue_free(phba, q); @@ -3541,69 +3584,6 @@ static void be_mcc_queues_destroy(struct beiscsi_hba *phba) } } -static void hwi_cleanup_port(struct beiscsi_hba *phba) -{ - struct be_queue_info *q; - struct be_ctrl_info *ctrl = &phba->ctrl; - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - struct hwi_async_pdu_context *pasync_ctx; - int i, eq_for_mcc, ulp_num; - - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - - be_cmd_iscsi_remove_template_hdr(ctrl); - - for (i = 0; i < phba->params.cxns_per_ctrl; i++) { - q = &phwi_context->be_wrbq[i]; - if (q->created) - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ); - } - kfree(phwi_context->be_wrbq); - free_wrb_handles(phba); - - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { - if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { - - q = &phwi_context->be_def_hdrq[ulp_num]; - if (q->created) - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); - - q = &phwi_context->be_def_dataq[ulp_num]; - if (q->created) - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); - - pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num]; - } - } - - beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); - - for (i = 0; i < (phba->num_cpus); i++) { - q = &phwi_context->be_cq[i]; - if (q->created) { - be_queue_free(phba, q); - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); - } - } - - be_mcc_queues_destroy(phba); - if (phba->msix_enabled) - eq_for_mcc = 1; - else - eq_for_mcc = 0; - for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) { - q = &phwi_context->be_eq[i].q; - if (q->created) { - be_queue_free(phba, q); - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); - } - } - /* last communication, indicate driver is unloading */ - beiscsi_cmd_special_wrb(&phba->ctrl, 0); -} - static int be_mcc_queues_create(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context) { @@ -3685,6 +3665,115 @@ static void find_num_cpus(struct beiscsi_hba *phba) } } +static void hwi_purge_eq(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_queue_info *eq; + struct be_eq_entry *eqe = NULL; + int i, eq_msix; + unsigned int num_processed; + + if (beiscsi_hba_in_error(phba)) + return; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + if (phba->msix_enabled) + eq_msix = 1; + else + eq_msix = 0; + + for (i = 0; i < (phba->num_cpus + eq_msix); i++) { + eq = &phwi_context->be_eq[i].q; + eqe = queue_tail_node(eq); + num_processed = 0; + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_processed++; + } + + if (num_processed) + hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1); + } +} + +static void hwi_cleanup_port(struct beiscsi_hba *phba) +{ + struct be_queue_info *q; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct hwi_async_pdu_context *pasync_ctx; + int i, eq_for_mcc, ulp_num; + + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) + if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) + beiscsi_cmd_iscsi_cleanup(phba, ulp_num); + + /** + * Purge all EQ entries that may have been left out. This is to + * workaround a problem we've seen occasionally where driver gets an + * interrupt with EQ entry bit set after stopping the controller. + */ + hwi_purge_eq(phba); + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + + be_cmd_iscsi_remove_template_hdr(ctrl); + + for (i = 0; i < phba->params.cxns_per_ctrl; i++) { + q = &phwi_context->be_wrbq[i]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ); + } + kfree(phwi_context->be_wrbq); + free_wrb_handles(phba); + + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { + if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { + + q = &phwi_context->be_def_hdrq[ulp_num]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); + + q = &phwi_context->be_def_dataq[ulp_num]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); + + pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num]; + } + } + + beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); + + for (i = 0; i < (phba->num_cpus); i++) { + q = &phwi_context->be_cq[i]; + if (q->created) { + be_queue_free(phba, q); + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); + } + } + + be_mcc_queues_destroy(phba); + if (phba->msix_enabled) + eq_for_mcc = 1; + else + eq_for_mcc = 0; + for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) { + q = &phwi_context->be_eq[i].q; + if (q->created) { + be_queue_free(phba, q); + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); + } + } + /* last communication, indicate driver is unloading */ + beiscsi_cmd_special_wrb(&phba->ctrl, 0); +} static int hwi_init_port(struct beiscsi_hba *phba) { struct hwi_controller *phwi_ctrlr; @@ -4206,50 +4295,11 @@ do_cleanup_ctrlr: return ret; } -static void hwi_purge_eq(struct beiscsi_hba *phba) -{ - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - struct be_queue_info *eq; - struct be_eq_entry *eqe = NULL; - int i, eq_msix; - unsigned int num_processed; - - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - if (phba->msix_enabled) - eq_msix = 1; - else - eq_msix = 0; - - for (i = 0; i < (phba->num_cpus + eq_msix); i++) { - eq = &phwi_context->be_eq[i].q; - eqe = queue_tail_node(eq); - num_processed = 0; - while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] - & EQE_VALID_MASK) { - AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); - queue_tail_inc(eq); - eqe = queue_tail_node(eq); - num_processed++; - } - - if (num_processed) - hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1); - } -} - static void beiscsi_cleanup_port(struct beiscsi_hba *phba) { struct ulp_cid_info *ptr_cid_info = NULL; int ulp_num; - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) - if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) - beiscsi_cmd_iscsi_cleanup(phba, ulp_num); - - hwi_purge_eq(phba); - hwi_cleanup_port(phba); kfree(phba->io_sgl_hndl_base); kfree(phba->eh_sgl_hndl_base); kfree(phba->ep_array); @@ -4266,7 +4316,6 @@ static void beiscsi_cleanup_port(struct beiscsi_hba *phba) } } } - } /** @@ -4840,7 +4889,7 @@ static int beiscsi_task_xmit(struct iscsi_task *task) * operational if FW still gets heartbeat from EP FW. Is management * path really needed to continue further? */ - if (beiscsi_hba_in_error(phba)) + if (!beiscsi_hba_is_online(phba)) return -EIO; if (!io_task->conn->login_in_progress) @@ -4896,7 +4945,7 @@ static int beiscsi_bsg_request(struct bsg_job *job) shost = iscsi_job_to_shost(job); phba = iscsi_host_priv(shost); - if (beiscsi_hba_in_error(phba)) { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BM_%d : HBA in error 0x%lx\n", phba->state); return -ENXIO; @@ -4929,6 +4978,14 @@ static int beiscsi_bsg_request(struct bsg_job *job) phba->ctrl.mcc_tag_status[tag], msecs_to_jiffies( BEISCSI_HOST_MBX_TIMEOUT)); + + if (!test_bit(BEISCSI_HBA_ONLINE, &phba->state)) { + clear_bit(MCC_TAG_STATE_RUNNING, + &phba->ctrl.ptag_state[tag].tag_state); + pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, + nonemb_cmd.va, nonemb_cmd.dma); + return -EIO; + } extd_status = (phba->ctrl.mcc_tag_status[tag] & CQE_STATUS_ADDL_MASK) >> CQE_STATUS_ADDL_SHIFT; status = phba->ctrl.mcc_tag_status[tag] & CQE_STATUS_MASK; @@ -4972,98 +5029,6 @@ void beiscsi_hba_attrs_init(struct beiscsi_hba *phba) beiscsi_log_enable_init(phba, beiscsi_log_enable); } -/* - * beiscsi_quiesce()- Cleanup Driver resources - * @phba: Instance Priv structure - * - * Free the OS and HW resources held by the driver - **/ -static void beiscsi_quiesce(struct beiscsi_hba *phba) -{ - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - struct be_eq_obj *pbe_eq; - unsigned int i, msix_vec; - - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - hwi_disable_intr(phba); - if (phba->msix_enabled) { - for (i = 0; i <= phba->num_cpus; i++) { - msix_vec = phba->msix_entries[i].vector; - free_irq(msix_vec, &phwi_context->be_eq[i]); - kfree(phba->msi_name[i]); - } - } else - if (phba->pcidev->irq) - free_irq(phba->pcidev->irq, phba); - pci_disable_msix(phba->pcidev); - cancel_delayed_work_sync(&phba->eqd_update); - cancel_work_sync(&phba->boot_work); - del_timer_sync(&phba->hw_check); - - for (i = 0; i < phba->num_cpus; i++) { - pbe_eq = &phwi_context->be_eq[i]; - irq_poll_disable(&pbe_eq->iopoll); - } - - /* PCI_ERR is set then check if driver is not unloading */ - if (test_bit(BEISCSI_HBA_RUNNING, &phba->state) && - test_bit(BEISCSI_HBA_PCI_ERR, &phba->state)) { - hwi_cleanup_port(phba); - return; - } - - destroy_workqueue(phba->wq); - beiscsi_cleanup_port(phba); - beiscsi_free_mem(phba); - - beiscsi_unmap_pci_function(phba); - pci_free_consistent(phba->pcidev, - phba->ctrl.mbox_mem_alloced.size, - phba->ctrl.mbox_mem_alloced.va, - phba->ctrl.mbox_mem_alloced.dma); -} - -static void beiscsi_remove(struct pci_dev *pcidev) -{ - struct beiscsi_hba *phba = NULL; - - phba = pci_get_drvdata(pcidev); - if (!phba) { - dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n"); - return; - } - - clear_bit(BEISCSI_HBA_RUNNING, &phba->state); - beiscsi_iface_destroy_default(phba); - iscsi_host_remove(phba->shost); - beiscsi_quiesce(phba); - /* after cancelling boot_work */ - iscsi_boot_destroy_kset(phba->boot_struct.boot_kset); - pci_dev_put(phba->pcidev); - iscsi_host_free(phba->shost); - pci_disable_pcie_error_reporting(pcidev); - pci_set_drvdata(pcidev, NULL); - pci_release_regions(pcidev); - pci_disable_device(pcidev); -} - -static void beiscsi_msix_enable(struct beiscsi_hba *phba) -{ - int i, status; - - for (i = 0; i <= phba->num_cpus; i++) - phba->msix_entries[i].entry = i; - - status = pci_enable_msix_range(phba->pcidev, phba->msix_entries, - phba->num_cpus + 1, phba->num_cpus + 1); - if (status > 0) - phba->msix_enabled = true; - - return; -} - void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle) { if (phba->boot_struct.boot_kset) @@ -5179,7 +5144,6 @@ static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf) return rc; } - static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type) { umode_t rc = 0; @@ -5212,7 +5176,6 @@ static umode_t beiscsi_ini_get_attr_visibility(void *data, int type) return rc; } - static umode_t beiscsi_eth_get_attr_visibility(void *data, int type) { umode_t rc = 0; @@ -5300,7 +5263,7 @@ static void beiscsi_boot_work(struct work_struct *work) struct boot_struct *bs = &phba->boot_struct; unsigned int tag = 0; - if (beiscsi_hba_in_error(phba)) + if (!beiscsi_hba_is_online(phba)) return; beiscsi_log(phba, KERN_INFO, @@ -5339,19 +5302,6 @@ static void beiscsi_boot_work(struct work_struct *work) } } -static void beiscsi_hw_health_check(unsigned long ptr) -{ - struct beiscsi_hba *phba; - - phba = (struct beiscsi_hba *)ptr; - beiscsi_ue_detect(phba); - if (test_bit(BEISCSI_HBA_IN_UE, &phba->state)) - return; - - mod_timer(&phba->hw_check, - jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); -} - static void beiscsi_eqd_update_work(struct work_struct *work) { struct hwi_context_memory *phwi_context; @@ -5365,7 +5315,7 @@ static void beiscsi_eqd_update_work(struct work_struct *work) unsigned long now; phba = container_of(work, struct beiscsi_hba, eqd_update.work); - if (beiscsi_hba_in_error(phba)) + if (!beiscsi_hba_is_online(phba)) return; phwi_ctrlr = phba->phwi_ctrlr; @@ -5408,6 +5358,219 @@ static void beiscsi_eqd_update_work(struct work_struct *work) msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); } +static void beiscsi_msix_enable(struct beiscsi_hba *phba) +{ + int i, status; + + for (i = 0; i <= phba->num_cpus; i++) + phba->msix_entries[i].entry = i; + + status = pci_enable_msix_range(phba->pcidev, phba->msix_entries, + phba->num_cpus + 1, phba->num_cpus + 1); + if (status > 0) + phba->msix_enabled = true; +} + +static void beiscsi_hw_tpe_check(unsigned long ptr) +{ + struct beiscsi_hba *phba; + u32 wait; + + phba = (struct beiscsi_hba *)ptr; + /* if not TPE, do nothing */ + if (!beiscsi_detect_tpe(phba)) + return; + + /* wait default 4000ms before recovering */ + wait = 4000; + if (phba->ue2rp > BEISCSI_UE_DETECT_INTERVAL) + wait = phba->ue2rp - BEISCSI_UE_DETECT_INTERVAL; + queue_delayed_work(phba->wq, &phba->recover_port, + msecs_to_jiffies(wait)); +} + +static void beiscsi_hw_health_check(unsigned long ptr) +{ + struct beiscsi_hba *phba; + + phba = (struct beiscsi_hba *)ptr; + beiscsi_detect_ue(phba); + if (beiscsi_detect_ue(phba)) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d : port in error: %lx\n", phba->state); + /* detect TPE if UER supported */ + if (!test_bit(BEISCSI_HBA_UER_SUPP, &phba->state)) + return; + /* modify this timer to check TPE */ + phba->hw_check.function = beiscsi_hw_tpe_check; + } + + mod_timer(&phba->hw_check, + jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); +} + +/* + * beiscsi_enable_port()- Enables the disabled port. + * Only port resources freed in disable function are reallocated. + * This is called in HBA error handling path. + * + * @phba: Instance of driver private structure + * + **/ +static int beiscsi_enable_port(struct beiscsi_hba *phba) +{ + struct hwi_context_memory *phwi_context; + struct hwi_controller *phwi_ctrlr; + struct be_eq_obj *pbe_eq; + int ret, i; + + if (test_bit(BEISCSI_HBA_ONLINE, &phba->state)) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d : %s : port is online %lx\n", + __func__, phba->state); + return 0; + } + + ret = beiscsi_init_sliport(phba); + if (ret) + return ret; + + if (enable_msix) + find_num_cpus(phba); + else + phba->num_cpus = 1; + if (enable_msix) { + beiscsi_msix_enable(phba); + if (!phba->msix_enabled) + phba->num_cpus = 1; + } + + beiscsi_get_params(phba); + /* Re-enable UER. If different TPE occurs then it is recoverable. */ + beiscsi_set_uer_feature(phba); + + phba->shost->max_id = phba->params.cxns_per_ctrl; + phba->shost->can_queue = phba->params.ios_per_ctrl; + ret = hwi_init_controller(phba); + if (ret) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d : init controller failed %d\n", ret); + goto disable_msix; + } + + for (i = 0; i < MAX_MCC_CMD; i++) { + init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]); + phba->ctrl.mcc_tag[i] = i + 1; + phba->ctrl.mcc_tag_status[i + 1] = 0; + phba->ctrl.mcc_tag_available++; + } + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, be_iopoll); + } + + i = (phba->msix_enabled) ? i : 0; + /* Work item for MCC handling */ + pbe_eq = &phwi_context->be_eq[i]; + INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work); + + ret = beiscsi_init_irqs(phba); + if (ret < 0) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d : setup IRQs failed %d\n", ret); + goto cleanup_port; + } + hwi_enable_intr(phba); + /* port operational: clear all error bits */ + set_bit(BEISCSI_HBA_ONLINE, &phba->state); + __beiscsi_log(phba, KERN_INFO, + "BM_%d : port online: 0x%lx\n", phba->state); + + /* start hw_check timer and eqd_update work */ + schedule_delayed_work(&phba->eqd_update, + msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); + + /** + * Timer function gets modified for TPE detection. + * Always reinit to do health check first. + */ + phba->hw_check.function = beiscsi_hw_health_check; + mod_timer(&phba->hw_check, + jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); + return 0; + +cleanup_port: + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + irq_poll_disable(&pbe_eq->iopoll); + } + hwi_cleanup_port(phba); + +disable_msix: + if (phba->msix_enabled) + pci_disable_msix(phba->pcidev); + + return ret; +} + +/* + * beiscsi_disable_port()- Disable port and cleanup driver resources. + * This is called in HBA error handling and driver removal. + * @phba: Instance Priv structure + * @unload: indicate driver is unloading + * + * Free the OS and HW resources held by the driver + **/ +static void beiscsi_disable_port(struct beiscsi_hba *phba, int unload) +{ + struct hwi_context_memory *phwi_context; + struct hwi_controller *phwi_ctrlr; + struct be_eq_obj *pbe_eq; + unsigned int i, msix_vec; + + if (!test_and_clear_bit(BEISCSI_HBA_ONLINE, &phba->state)) + return; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + hwi_disable_intr(phba); + if (phba->msix_enabled) { + for (i = 0; i <= phba->num_cpus; i++) { + msix_vec = phba->msix_entries[i].vector; + free_irq(msix_vec, &phwi_context->be_eq[i]); + kfree(phba->msi_name[i]); + } + } else + if (phba->pcidev->irq) + free_irq(phba->pcidev->irq, phba); + pci_disable_msix(phba->pcidev); + + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + irq_poll_disable(&pbe_eq->iopoll); + } + cancel_delayed_work_sync(&phba->eqd_update); + cancel_work_sync(&phba->boot_work); + /* WQ might be running cancel queued mcc_work if we are not exiting */ + if (!unload && beiscsi_hba_in_error(phba)) { + pbe_eq = &phwi_context->be_eq[i]; + cancel_work_sync(&pbe_eq->mcc_work); + } + hwi_cleanup_port(phba); +} + +static void beiscsi_recover_port(struct work_struct *work) +{ + struct beiscsi_hba *phba; + + phba = container_of(work, struct beiscsi_hba, recover_port.work); + iscsi_host_for_each_session(phba->shost, beiscsi_session_fail); + beiscsi_disable_port(phba, 0); + beiscsi_enable_port(phba); +} static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev, pci_channel_state_t state) @@ -5420,7 +5583,11 @@ static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev, beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : EEH error detected\n"); - beiscsi_quiesce(phba); + /* first stop UE detection when PCI error detected */ + del_timer_sync(&phba->hw_check); + cancel_delayed_work_sync(&phba->recover_port); + + beiscsi_disable_port(phba, 0); if (state == pci_channel_io_perm_failure) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, @@ -5476,82 +5643,16 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev) static void beiscsi_eeh_resume(struct pci_dev *pdev) { - int ret, i; - struct be_eq_obj *pbe_eq; - struct beiscsi_hba *phba = NULL; - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; + struct beiscsi_hba *phba; + int ret; phba = (struct beiscsi_hba *)pci_get_drvdata(pdev); pci_save_state(pdev); - if (enable_msix) - find_num_cpus(phba); - else - phba->num_cpus = 1; - - if (enable_msix) { - beiscsi_msix_enable(phba); - if (!phba->msix_enabled) - phba->num_cpus = 1; - } - - ret = beiscsi_init_sliport(phba); + ret = beiscsi_enable_port(phba); if (ret) - goto ret_err; - - beiscsi_get_params(phba); - phba->shost->max_id = phba->params.cxns_per_ctrl; - phba->shost->can_queue = phba->params.ios_per_ctrl; - ret = hwi_init_controller(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : beiscsi_eeh_resume -" - "Failed to initialize beiscsi_hba.\n"); - goto ret_err; - } - - for (i = 0; i < MAX_MCC_CMD; i++) { - init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]); - phba->ctrl.mcc_tag[i] = i + 1; - phba->ctrl.mcc_tag_status[i + 1] = 0; - phba->ctrl.mcc_tag_available++; - } - - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - - for (i = 0; i < phba->num_cpus; i++) { - pbe_eq = &phwi_context->be_eq[i]; - irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, - be_iopoll); - } - - i = (phba->msix_enabled) ? i : 0; - /* Work item for MCC handling */ - pbe_eq = &phwi_context->be_eq[i]; - INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work); - - ret = beiscsi_init_irqs(phba); - if (ret < 0) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : beiscsi_eeh_resume - " - "Failed to beiscsi_init_irqs\n"); - goto ret_err; - } - - hwi_enable_intr(phba); - clear_bit(BEISCSI_HBA_PCI_ERR, &phba->state); - - /* start hw_check timer and eqd_update work */ - schedule_delayed_work(&phba->eqd_update, - msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); - mod_timer(&phba->hw_check, - jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); - return; -ret_err: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : AER EEH Resume Failed\n"); + __beiscsi_log(phba, KERN_ERR, + "BM_%d : AER EEH resume failed\n"); } static int beiscsi_dev_probe(struct pci_dev *pcidev, @@ -5623,7 +5724,6 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, if (ret) goto hba_free; - set_bit(BEISCSI_HBA_RUNNING, &phba->state); spin_lock_init(&phba->io_sgl_lock); spin_lock_init(&phba->mgmt_sgl_lock); spin_lock_init(&phba->async_pdu_lock); @@ -5690,8 +5790,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, for (i = 0; i < phba->num_cpus; i++) { pbe_eq = &phwi_context->be_eq[i]; - irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, - be_iopoll); + irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, be_iopoll); } i = (phba->msix_enabled) ? i : 0; @@ -5708,9 +5807,15 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, } hwi_enable_intr(phba); - if (iscsi_host_add(phba->shost, &phba->pcidev->dev)) + ret = iscsi_host_add(phba->shost, &phba->pcidev->dev); + if (ret) goto free_blkenbld; + /* set online bit after port is operational */ + set_bit(BEISCSI_HBA_ONLINE, &phba->state); + __beiscsi_log(phba, KERN_INFO, + "BM_%d : port online: 0x%lx\n", phba->state); + INIT_WORK(&phba->boot_work, beiscsi_boot_work); ret = beiscsi_boot_get_shandle(phba, &s_handle); if (ret > 0) { @@ -5726,6 +5831,8 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, beiscsi_iface_create_default(phba); schedule_delayed_work(&phba->eqd_update, msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); + + INIT_DELAYED_WORK(&phba->recover_port, beiscsi_recover_port); /** * Start UE detection here. UE before this will cause stall in probe * and eventually fail the probe. @@ -5747,6 +5854,7 @@ free_blkenbld: irq_poll_disable(&pbe_eq->iopoll); } free_twq: + hwi_cleanup_port(phba); beiscsi_cleanup_port(phba); beiscsi_free_mem(phba); free_port: @@ -5767,6 +5875,48 @@ disable_pci: return ret; } +static void beiscsi_remove(struct pci_dev *pcidev) +{ + struct beiscsi_hba *phba = NULL; + + phba = pci_get_drvdata(pcidev); + if (!phba) { + dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n"); + return; + } + + /* first stop UE detection before unloading */ + del_timer_sync(&phba->hw_check); + cancel_delayed_work_sync(&phba->recover_port); + + beiscsi_iface_destroy_default(phba); + iscsi_host_remove(phba->shost); + beiscsi_disable_port(phba, 1); + + /* after cancelling boot_work */ + iscsi_boot_destroy_kset(phba->boot_struct.boot_kset); + + /* free all resources */ + destroy_workqueue(phba->wq); + beiscsi_cleanup_port(phba); + beiscsi_free_mem(phba); + + /* ctrl uninit */ + beiscsi_unmap_pci_function(phba); + pci_free_consistent(phba->pcidev, + phba->ctrl.mbox_mem_alloced.size, + phba->ctrl.mbox_mem_alloced.va, + phba->ctrl.mbox_mem_alloced.dma); + + pci_dev_put(phba->pcidev); + iscsi_host_free(phba->shost); + pci_disable_pcie_error_reporting(pcidev); + pci_set_drvdata(pcidev, NULL); + pci_release_regions(pcidev); + pci_disable_device(pcidev); +} + + static struct pci_error_handlers beiscsi_eeh_handlers = { .error_detected = beiscsi_eeh_err_detected, .slot_reset = beiscsi_eeh_reset, @@ -5814,7 +5964,6 @@ static struct pci_driver beiscsi_pci_driver = { .err_handler = &beiscsi_eeh_handlers }; - static int __init beiscsi_module_init(void) { int ret; diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 4cdb34c3de5d..1fd6c1868366 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -393,7 +393,7 @@ struct beiscsi_hba { } fw_config; unsigned long state; -#define BEISCSI_HBA_RUNNING 0 +#define BEISCSI_HBA_ONLINE 0 #define BEISCSI_HBA_LINK_UP 1 #define BEISCSI_HBA_BOOT_FOUND 2 #define BEISCSI_HBA_BOOT_WORK 3 @@ -417,6 +417,7 @@ struct beiscsi_hba { /* check for UE every 1000ms */ #define BEISCSI_UE_DETECT_INTERVAL 1000 u32 ue2rp; + struct delayed_work recover_port; bool mac_addr_set; u8 mac_address[ETH_ALEN]; @@ -455,6 +456,9 @@ struct beiscsi_hba { }; #define beiscsi_hba_in_error(phba) ((phba)->state & BEISCSI_HBA_IN_ERR) +#define beiscsi_hba_is_online(phba) \ + (!beiscsi_hba_in_error((phba)) && \ + test_bit(BEISCSI_HBA_ONLINE, &phba->state)) struct beiscsi_session { struct pci_pool *bhs_pool; diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index a844299ea979..736eca38ea0b 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -24,134 +24,6 @@ #include "be_iscsi.h" #include "be_main.h" -/* UE Status Low CSR */ -static const char * const desc_ue_status_low[] = { - "CEV", - "CTX", - "DBUF", - "ERX", - "Host", - "MPU", - "NDMA", - "PTC ", - "RDMA ", - "RXF ", - "RXIPS ", - "RXULP0 ", - "RXULP1 ", - "RXULP2 ", - "TIM ", - "TPOST ", - "TPRE ", - "TXIPS ", - "TXULP0 ", - "TXULP1 ", - "UC ", - "WDMA ", - "TXULP2 ", - "HOST1 ", - "P0_OB_LINK ", - "P1_OB_LINK ", - "HOST_GPIO ", - "MBOX ", - "AXGMAC0", - "AXGMAC1", - "JTAG", - "MPU_INTPEND" -}; - -/* UE Status High CSR */ -static const char * const desc_ue_status_hi[] = { - "LPCMEMHOST", - "MGMT_MAC", - "PCS0ONLINE", - "MPU_IRAM", - "PCS1ONLINE", - "PCTL0", - "PCTL1", - "PMEM", - "RR", - "TXPB", - "RXPP", - "XAUI", - "TXP", - "ARM", - "IPC", - "HOST2", - "HOST3", - "HOST4", - "HOST5", - "HOST6", - "HOST7", - "HOST8", - "HOST9", - "NETC", - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown" -}; - -/* - * beiscsi_ue_detect()- Detect Unrecoverable Error on adapter - * @phba: Driver priv structure - * - * Read registers linked to UE and check for the UE status - **/ -void beiscsi_ue_detect(struct beiscsi_hba *phba) -{ - uint32_t ue_hi = 0, ue_lo = 0; - uint32_t ue_mask_hi = 0, ue_mask_lo = 0; - uint8_t i = 0; - - pci_read_config_dword(phba->pcidev, - PCICFG_UE_STATUS_LOW, &ue_lo); - pci_read_config_dword(phba->pcidev, - PCICFG_UE_STATUS_MASK_LOW, - &ue_mask_lo); - pci_read_config_dword(phba->pcidev, - PCICFG_UE_STATUS_HIGH, - &ue_hi); - pci_read_config_dword(phba->pcidev, - PCICFG_UE_STATUS_MASK_HI, - &ue_mask_hi); - - ue_lo = (ue_lo & ~ue_mask_lo); - ue_hi = (ue_hi & ~ue_mask_hi); - - - if (ue_lo || ue_hi) { - set_bit(BEISCSI_HBA_IN_UE, &phba->state); - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : HBA error detected\n"); - } - - if (ue_lo) { - for (i = 0; ue_lo; ue_lo >>= 1, i++) { - if (ue_lo & 1) - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG, - "BG_%d : UE_LOW %s bit set\n", - desc_ue_status_low[i]); - } - } - - if (ue_hi) { - for (i = 0; ue_hi; ue_hi >>= 1, i++) { - if (ue_hi & 1) - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG, - "BG_%d : UE_HIGH %s bit set\n", - desc_ue_status_hi[i]); - } - } -} - int beiscsi_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *set_eqd, int num) diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index dab128fb2ce3..ee6fd7e663cd 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -329,7 +329,6 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, struct wrb_handle *pwrb_handle, struct hwi_wrb_context *pwrb_context); -void beiscsi_ue_detect(struct beiscsi_hba *phba); int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *, int num); From 10e1a44ab2bfc850f5ccca5624c68f7c7f8c460c Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:19 +0530 Subject: [PATCH 050/173] scsi: be2iscsi: Fail the sessions immediately after TPE Sessions are no longer valid, so schedule sess_work to fail the sessions immediately when error is detected. This is done to avoid iSCSI transport layer to keep sending NOP-Out which driver any ways fail. Schedule sess_work immediately in case of HBA error. Old sessions are gone for good and need to be re-established. iscsi_session_failure needs process context hence this work. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_main.c | 24 +++++++++++++++++++++--- drivers/scsi/be2iscsi/be_main.h | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 0625bd0c449f..cf82973e2789 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -5398,7 +5398,10 @@ static void beiscsi_hw_health_check(unsigned long ptr) if (beiscsi_detect_ue(phba)) { __beiscsi_log(phba, KERN_ERR, "BM_%d : port in error: %lx\n", phba->state); - /* detect TPE if UER supported */ + /* sessions are no longer valid, so first fail the sessions */ + queue_work(phba->wq, &phba->sess_work); + + /* detect UER supported */ if (!test_bit(BEISCSI_HBA_UER_SUPP, &phba->state)) return; /* modify this timer to check TPE */ @@ -5562,12 +5565,24 @@ static void beiscsi_disable_port(struct beiscsi_hba *phba, int unload) hwi_cleanup_port(phba); } +static void beiscsi_sess_work(struct work_struct *work) +{ + struct beiscsi_hba *phba; + + phba = container_of(work, struct beiscsi_hba, sess_work); + /* + * This work gets scheduled only in case of HBA error. + * Old sessions are gone so need to be re-established. + * iscsi_session_failure needs process context hence this work. + */ + iscsi_host_for_each_session(phba->shost, beiscsi_session_fail); +} + static void beiscsi_recover_port(struct work_struct *work) { struct beiscsi_hba *phba; phba = container_of(work, struct beiscsi_hba, recover_port.work); - iscsi_host_for_each_session(phba->shost, beiscsi_session_fail); beiscsi_disable_port(phba, 0); beiscsi_enable_port(phba); } @@ -5587,6 +5602,8 @@ static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev, del_timer_sync(&phba->hw_check); cancel_delayed_work_sync(&phba->recover_port); + /* sessions are no longer valid, so first fail the sessions */ + iscsi_host_for_each_session(phba->shost, beiscsi_session_fail); beiscsi_disable_port(phba, 0); if (state == pci_channel_io_perm_failure) { @@ -5832,6 +5849,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, schedule_delayed_work(&phba->eqd_update, msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); + INIT_WORK(&phba->sess_work, beiscsi_sess_work); INIT_DELAYED_WORK(&phba->recover_port, beiscsi_recover_port); /** * Start UE detection here. UE before this will cause stall in probe @@ -5842,7 +5860,6 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, phba->hw_check.data = (unsigned long)phba; mod_timer(&phba->hw_check, jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, "\n\n\n BM_%d : SUCCESS - DRIVER LOADED\n\n\n"); return 0; @@ -5888,6 +5905,7 @@ static void beiscsi_remove(struct pci_dev *pcidev) /* first stop UE detection before unloading */ del_timer_sync(&phba->hw_check); cancel_delayed_work_sync(&phba->recover_port); + cancel_work_sync(&phba->sess_work); beiscsi_iface_destroy_default(phba); iscsi_host_remove(phba->shost); diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 1fd6c1868366..953d94a84f9c 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -418,6 +418,7 @@ struct beiscsi_hba { #define BEISCSI_UE_DETECT_INTERVAL 1000 u32 ue2rp; struct delayed_work recover_port; + struct work_struct sess_work; bool mac_addr_set; u8 mac_address[ETH_ALEN]; From 4ee1ec42b33036c785d92744446630e6e242327e Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:20 +0530 Subject: [PATCH 051/173] scsi: be2iscsi: Add FUNCTION_RESET during driver unload Driver unload should call COMMON_FUNCTION_RESET. For TPE feature, this ensures that FW has knowledge about driver getting unloaded and can reset its bit vector. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_cmds.c | 2 +- drivers/scsi/be2iscsi/be_cmds.h | 2 ++ drivers/scsi/be2iscsi/be_main.c | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index ad7405d6821f..5fe161029585 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -1595,7 +1595,7 @@ int beiscsi_check_fw_rdy(struct beiscsi_hba *phba) return rdy; } -static int beiscsi_cmd_function_reset(struct beiscsi_hba *phba) +int beiscsi_cmd_function_reset(struct beiscsi_hba *phba) { struct be_ctrl_info *ctrl = &phba->ctrl; struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index 26d7921f48a4..8283344c3f93 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -762,6 +762,8 @@ struct be_cmd_set_features { } param; } __packed; +int beiscsi_cmd_function_reset(struct beiscsi_hba *phba); + int beiscsi_cmd_special_wrb(struct be_ctrl_info *ctrl, u32 load); int beiscsi_check_fw_rdy(struct beiscsi_hba *phba); diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index cf82973e2789..3aa2f04e71a7 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -3771,9 +3771,12 @@ static void hwi_cleanup_port(struct beiscsi_hba *phba) beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); } } + /* this ensures complete FW cleanup */ + beiscsi_cmd_function_reset(phba); /* last communication, indicate driver is unloading */ beiscsi_cmd_special_wrb(&phba->ctrl, 0); } + static int hwi_init_port(struct beiscsi_hba *phba) { struct hwi_controller *phwi_ctrlr; From 938f372c7d3d2a9a732a1ed7c9a0131ab1ab2e5b Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:21 +0530 Subject: [PATCH 052/173] scsi: be2iscsi: Fix async PDU handling path BUG: unable to handle kernel NULL pointer dereference at 000000000000015e IP: [] hwi_get_async_handle.isra.23.constprop.39+0x90/0x1d0 [be2iscsi] PGD 0 Oops: 0000 [#1] SMP ... Call Trace: [] hwi_process_default_pdu_ring+0x7c/0x280 [be2iscsi] [] beiscsi_process_cq+0x321/0xb90 [be2iscsi] [] ? __wake_up_common+0x58/0x90 [] ? __wake_up+0x44/0x50 [] be_iopoll+0x1d/0xb0 [be2iscsi] [] blk_iopoll_softirq+0xc1/0x100 [] __do_softirq+0xef/0x280 The symptom observed is multiple async handles get queued for same index thus causing leak in buffers posted to FW. The root cause is: - async handle is continued to be used even if it does not match the completion. - list_move operation done on already filled index. 1. Remove use of writables, host_write_ptr and ep_read_ptr. 2. Remove consumed logic to update writables. Instead, use only free_entries to do the accounting of handles to be posted back. 3. Remove busy_list, instead use simple slot to index handles. 4. Added check no data, header less and overflow to make sure all async_handles are flushed in error cases. 5. Added code to verify gathering of handles to form PDU by checking final bit before forwarding PDU. 6. Added code to catch mismatch with CQE and handle gracefully. 7. Use AMAP, traverse cri_wait_queue list to post buffers, log "async PDU" related errors. 8. Rearranged few data structures and added comments in init & processing path. 9. Added WARN_ONs to catch any HD ring corruption. Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_main.c | 902 +++++++++++++++----------------- drivers/scsi/be2iscsi/be_main.h | 124 ++--- 2 files changed, 491 insertions(+), 535 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 3aa2f04e71a7..5024651e3a68 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -897,57 +897,6 @@ void hwi_ring_cq_db(struct beiscsi_hba *phba, iowrite32(val, phba->db_va + DB_CQ_OFFSET); } -static unsigned int -beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, - struct pdu_base *ppdu, - unsigned long pdu_len, - void *pbuffer, unsigned long buf_len) -{ - struct iscsi_conn *conn = beiscsi_conn->conn; - struct iscsi_session *session = conn->session; - struct iscsi_task *task; - struct beiscsi_io_task *io_task; - struct iscsi_hdr *login_hdr; - - switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] & - PDUBASE_OPCODE_MASK) { - case ISCSI_OP_NOOP_IN: - pbuffer = NULL; - buf_len = 0; - break; - case ISCSI_OP_ASYNC_EVENT: - break; - case ISCSI_OP_REJECT: - WARN_ON(!pbuffer); - WARN_ON(!(buf_len == 48)); - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO, - "BM_%d : In ISCSI_OP_REJECT\n"); - break; - case ISCSI_OP_LOGIN_RSP: - case ISCSI_OP_TEXT_RSP: - task = conn->login_task; - io_task = task->dd_data; - login_hdr = (struct iscsi_hdr *)ppdu; - login_hdr->itt = io_task->libiscsi_itt; - break; - default: - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG, - "BM_%d : Unrecognized opcode 0x%x in async msg\n", - (ppdu-> - dw[offsetof(struct amap_pdu_base, opcode) / 32] - & PDUBASE_OPCODE_MASK)); - return 1; - } - - spin_lock_bh(&session->back_lock); - __iscsi_complete_pdu(conn, (struct iscsi_hdr *)ppdu, pbuffer, buf_len); - spin_unlock_bh(&session->back_lock); - return 0; -} - static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba) { struct sgl_handle *psgl_handle; @@ -1434,431 +1383,428 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn, spin_unlock_bh(&session->back_lock); } -static struct list_head *hwi_get_async_busy_list(struct hwi_async_pdu_context - *pasync_ctx, unsigned int is_header, - unsigned int host_write_ptr) -{ - if (is_header) - return &pasync_ctx->async_entry[host_write_ptr]. - header_busy_list; - else - return &pasync_ctx->async_entry[host_write_ptr].data_busy_list; -} - -static struct async_pdu_handle * -hwi_get_async_handle(struct beiscsi_hba *phba, - struct beiscsi_conn *beiscsi_conn, - struct hwi_async_pdu_context *pasync_ctx, - struct i_t_dpdu_cqe *pdpdu_cqe, unsigned int *pcq_index) -{ - struct be_bus_address phys_addr; - struct list_head *pbusy_list; - struct async_pdu_handle *pasync_handle = NULL; - unsigned char is_header = 0; - unsigned int index, dpl; - - if (is_chip_be2_be3r(phba)) { - dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, - dpl, pdpdu_cqe); - index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, - index, pdpdu_cqe); - } else { - dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, - dpl, pdpdu_cqe); - index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, - index, pdpdu_cqe); - } - - phys_addr.u.a32.address_lo = - (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, - db_addr_lo) / 32] - dpl); - phys_addr.u.a32.address_hi = - pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, - db_addr_hi) / 32]; - - phys_addr.u.a64.address = - *((unsigned long long *)(&phys_addr.u.a64.address)); - - switch (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, code) / 32] - & PDUCQE_CODE_MASK) { - case UNSOL_HDR_NOTIFY: - is_header = 1; - - pbusy_list = hwi_get_async_busy_list(pasync_ctx, - is_header, index); - break; - case UNSOL_DATA_NOTIFY: - pbusy_list = hwi_get_async_busy_list(pasync_ctx, - is_header, index); - break; - default: - pbusy_list = NULL; - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG, - "BM_%d : Unexpected code=%d\n", - pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, - code) / 32] & PDUCQE_CODE_MASK); - return NULL; - } - - WARN_ON(list_empty(pbusy_list)); - list_for_each_entry(pasync_handle, pbusy_list, link) { - if (pasync_handle->pa.u.a64.address == phys_addr.u.a64.address) - break; - } - - WARN_ON(!pasync_handle); - - pasync_handle->cri = BE_GET_ASYNC_CRI_FROM_CID( - beiscsi_conn->beiscsi_conn_cid); - pasync_handle->is_header = is_header; - pasync_handle->buffer_len = dpl; - *pcq_index = index; - - return pasync_handle; -} - +/** + * ASYNC PDUs include + * a. Unsolicited NOP-In (target initiated NOP-In) + * b. ASYNC Messages + * c. Reject PDU + * d. Login response + * These headers arrive unprocessed by the EP firmware. + * iSCSI layer processes them. + */ static unsigned int -hwi_update_async_writables(struct beiscsi_hba *phba, - struct hwi_async_pdu_context *pasync_ctx, - unsigned int is_header, unsigned int cq_index) +beiscsi_complete_pdu(struct beiscsi_conn *beiscsi_conn, + struct pdu_base *phdr, void *pdata, unsigned int dlen) { - struct list_head *pbusy_list; - struct async_pdu_handle *pasync_handle; - unsigned int num_entries, writables = 0; - unsigned int *pep_read_ptr, *pwritables; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct iscsi_conn *conn = beiscsi_conn->conn; + struct beiscsi_io_task *io_task; + struct iscsi_hdr *login_hdr; + struct iscsi_task *task; + u8 code; - num_entries = pasync_ctx->num_entries; - if (is_header) { - pep_read_ptr = &pasync_ctx->async_header.ep_read_ptr; - pwritables = &pasync_ctx->async_header.writables; - } else { - pep_read_ptr = &pasync_ctx->async_data.ep_read_ptr; - pwritables = &pasync_ctx->async_data.writables; - } - - while ((*pep_read_ptr) != cq_index) { - (*pep_read_ptr)++; - *pep_read_ptr = (*pep_read_ptr) % num_entries; - - pbusy_list = hwi_get_async_busy_list(pasync_ctx, is_header, - *pep_read_ptr); - if (writables == 0) - WARN_ON(list_empty(pbusy_list)); - - if (!list_empty(pbusy_list)) { - pasync_handle = list_entry(pbusy_list->next, - struct async_pdu_handle, - link); - WARN_ON(!pasync_handle); - pasync_handle->consumed = 1; - } - - writables++; - } - - if (!writables) { + code = AMAP_GET_BITS(struct amap_pdu_base, opcode, phdr); + switch (code) { + case ISCSI_OP_NOOP_IN: + pdata = NULL; + dlen = 0; + break; + case ISCSI_OP_ASYNC_EVENT: + break; + case ISCSI_OP_REJECT: + WARN_ON(!pdata); + WARN_ON(!(dlen == 48)); beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO, - "BM_%d : Duplicate notification received - index 0x%x!!\n", - cq_index); - WARN_ON(1); + "BM_%d : In ISCSI_OP_REJECT\n"); + break; + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + task = conn->login_task; + io_task = task->dd_data; + login_hdr = (struct iscsi_hdr *)phdr; + login_hdr->itt = io_task->libiscsi_itt; + break; + default: + beiscsi_log(phba, KERN_WARNING, + BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG, + "BM_%d : unrecognized async PDU opcode 0x%x\n", + code); + return 1; } - - *pwritables = *pwritables + writables; + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)phdr, pdata, dlen); return 0; } -static void hwi_free_async_msg(struct beiscsi_hba *phba, - struct hwi_async_pdu_context *pasync_ctx, - unsigned int cri) +static inline void +beiscsi_hdl_put_handle(struct hd_async_context *pasync_ctx, + struct hd_async_handle *pasync_handle) { - struct async_pdu_handle *pasync_handle, *tmp_handle; - struct list_head *plist; + if (pasync_handle->is_header) { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_header.free_list); + pasync_ctx->async_header.free_entries++; + } else { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_data.free_list); + pasync_ctx->async_data.free_entries++; + } +} - plist = &pasync_ctx->async_entry[cri].wait_queue.list; - list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) { - list_del(&pasync_handle->link); +static struct hd_async_handle * +beiscsi_hdl_get_handle(struct beiscsi_conn *beiscsi_conn, + struct hd_async_context *pasync_ctx, + struct i_t_dpdu_cqe *pdpdu_cqe) +{ + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct hd_async_handle *pasync_handle; + struct be_bus_address phys_addr; + u8 final, error = 0; + u16 cid, code, ci; + u32 dpl; - if (pasync_handle->is_header) { - list_add_tail(&pasync_handle->link, - &pasync_ctx->async_header.free_list); - pasync_ctx->async_header.free_entries++; - } else { - list_add_tail(&pasync_handle->link, - &pasync_ctx->async_data.free_list); - pasync_ctx->async_data.free_entries++; - } + cid = beiscsi_conn->beiscsi_conn_cid; + /** + * This function is invoked to get the right async_handle structure + * from a given DEF PDU CQ entry. + * + * - index in CQ entry gives the vertical index + * - address in CQ entry is the offset where the DMA last ended + * - final - no more notifications for this PDU + */ + if (is_chip_be2_be3r(phba)) { + dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + dpl, pdpdu_cqe); + ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + index, pdpdu_cqe); + final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + final, pdpdu_cqe); + } else { + dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, + dpl, pdpdu_cqe); + ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, + index, pdpdu_cqe); + final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, + final, pdpdu_cqe); } - INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wait_queue.list); - pasync_ctx->async_entry[cri].wait_queue.hdr_received = 0; - pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; -} + /** + * DB addr Hi/Lo is same for BE and SKH. + * Subtract the dataplacementlength to get to the base. + */ + phys_addr.u.a32.address_lo = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + db_addr_lo, pdpdu_cqe); + phys_addr.u.a32.address_lo -= dpl; + phys_addr.u.a32.address_hi = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + db_addr_hi, pdpdu_cqe); -static struct phys_addr * -hwi_get_ring_address(struct hwi_async_pdu_context *pasync_ctx, - unsigned int is_header, unsigned int host_write_ptr) -{ - struct phys_addr *pasync_sge = NULL; + code = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, code, pdpdu_cqe); + switch (code) { + case UNSOL_HDR_NOTIFY: + pasync_handle = pasync_ctx->async_entry[ci].header; + break; + case UNSOL_DATA_DIGEST_ERROR_NOTIFY: + error = 1; + case UNSOL_DATA_NOTIFY: + pasync_handle = pasync_ctx->async_entry[ci].data; + break; + /* called only for above codes */ + default: + pasync_handle = NULL; + break; + } - if (is_header) - pasync_sge = pasync_ctx->async_header.ring_base; + if (!pasync_handle) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : cid %d async PDU handle not found - code %d ci %d addr %llx\n", + cid, code, ci, phys_addr.u.a64.address); + return pasync_handle; + } + + if (pasync_handle->pa.u.a64.address != phys_addr.u.a64.address || + pasync_handle->index != ci) { + /* driver bug - if ci does not match async handle index */ + error = 1; + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : cid %u async PDU handle mismatch - addr in %cQE %llx at %u:addr in CQE %llx ci %u\n", + cid, pasync_handle->is_header ? 'H' : 'D', + pasync_handle->pa.u.a64.address, + pasync_handle->index, + phys_addr.u.a64.address, ci); + /* FW has stale address - attempt continuing by dropping */ + } + + /** + * Each CID is associated with unique CRI. + * ASYNC_CRI_FROM_CID mapping and CRI_FROM_CID are totaly different. + **/ + pasync_handle->cri = BE_GET_ASYNC_CRI_FROM_CID(cid); + pasync_handle->is_final = final; + pasync_handle->buffer_len = dpl; + /* empty the slot */ + if (pasync_handle->is_header) + pasync_ctx->async_entry[ci].header = NULL; else - pasync_sge = pasync_ctx->async_data.ring_base; + pasync_ctx->async_entry[ci].data = NULL; - return pasync_sge + host_write_ptr; + /** + * DEF PDU header and data buffers with errors should be simply + * dropped as there are no consumers for it. + */ + if (error) { + beiscsi_hdl_put_handle(pasync_ctx, pasync_handle); + pasync_handle = NULL; + } + return pasync_handle; } -static void hwi_post_async_buffers(struct beiscsi_hba *phba, - unsigned int is_header, uint8_t ulp_num) +static void +beiscsi_hdl_purge_handles(struct beiscsi_hba *phba, + struct hd_async_context *pasync_ctx, + u16 cri) { + struct hd_async_handle *pasync_handle, *tmp_handle; + struct list_head *plist; + + plist = &pasync_ctx->async_entry[cri].wq.list; + list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) { + list_del(&pasync_handle->link); + beiscsi_hdl_put_handle(pasync_ctx, pasync_handle); + } + + INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wq.list); + pasync_ctx->async_entry[cri].wq.hdr_len = 0; + pasync_ctx->async_entry[cri].wq.bytes_received = 0; + pasync_ctx->async_entry[cri].wq.bytes_needed = 0; +} + +static unsigned int +beiscsi_hdl_fwd_pdu(struct beiscsi_conn *beiscsi_conn, + struct hd_async_context *pasync_ctx, + u16 cri) +{ + struct iscsi_session *session = beiscsi_conn->conn->session; + struct hd_async_handle *pasync_handle, *plast_handle; + struct beiscsi_hba *phba = beiscsi_conn->phba; + void *phdr = NULL, *pdata = NULL; + u32 dlen = 0, status = 0; + struct list_head *plist; + + plist = &pasync_ctx->async_entry[cri].wq.list; + plast_handle = NULL; + list_for_each_entry(pasync_handle, plist, link) { + plast_handle = pasync_handle; + /* get the header, the first entry */ + if (!phdr) { + phdr = pasync_handle->pbuffer; + continue; + } + /* use first buffer to collect all the data */ + if (!pdata) { + pdata = pasync_handle->pbuffer; + dlen = pasync_handle->buffer_len; + continue; + } + memcpy(pdata + dlen, pasync_handle->pbuffer, + pasync_handle->buffer_len); + dlen += pasync_handle->buffer_len; + } + + if (!plast_handle->is_final) { + /* last handle should have final PDU notification from FW */ + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : cid %u %p fwd async PDU with last handle missing - HL%u:DN%u:DR%u\n", + beiscsi_conn->beiscsi_conn_cid, plast_handle, + pasync_ctx->async_entry[cri].wq.hdr_len, + pasync_ctx->async_entry[cri].wq.bytes_needed, + pasync_ctx->async_entry[cri].wq.bytes_received); + } + spin_lock_bh(&session->back_lock); + status = beiscsi_complete_pdu(beiscsi_conn, phdr, pdata, dlen); + spin_unlock_bh(&session->back_lock); + beiscsi_hdl_purge_handles(phba, pasync_ctx, cri); + return status; +} + +static unsigned int +beiscsi_hdl_gather_pdu(struct beiscsi_conn *beiscsi_conn, + struct hd_async_context *pasync_ctx, + struct hd_async_handle *pasync_handle) +{ + unsigned int bytes_needed = 0, status = 0; + u16 cri = pasync_handle->cri; + struct cri_wait_queue *wq; + struct beiscsi_hba *phba; + struct pdu_base *ppdu; + char *err = ""; + + phba = beiscsi_conn->phba; + wq = &pasync_ctx->async_entry[cri].wq; + if (pasync_handle->is_header) { + /* check if PDU hdr is rcv'd when old hdr not completed */ + if (wq->hdr_len) { + err = "incomplete"; + goto drop_pdu; + } + ppdu = pasync_handle->pbuffer; + bytes_needed = AMAP_GET_BITS(struct amap_pdu_base, + data_len_hi, ppdu); + bytes_needed <<= 16; + bytes_needed |= be16_to_cpu(AMAP_GET_BITS(struct amap_pdu_base, + data_len_lo, ppdu)); + wq->hdr_len = pasync_handle->buffer_len; + wq->bytes_received = 0; + wq->bytes_needed = bytes_needed; + list_add_tail(&pasync_handle->link, &wq->list); + if (!bytes_needed) + status = beiscsi_hdl_fwd_pdu(beiscsi_conn, + pasync_ctx, cri); + } else { + /* check if data received has header and is needed */ + if (!wq->hdr_len || !wq->bytes_needed) { + err = "header less"; + goto drop_pdu; + } + wq->bytes_received += pasync_handle->buffer_len; + /* Something got overwritten? Better catch it here. */ + if (wq->bytes_received > wq->bytes_needed) { + err = "overflow"; + goto drop_pdu; + } + list_add_tail(&pasync_handle->link, &wq->list); + if (wq->bytes_received == wq->bytes_needed) + status = beiscsi_hdl_fwd_pdu(beiscsi_conn, + pasync_ctx, cri); + } + return status; + +drop_pdu: + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : cid %u async PDU %s - def-%c:HL%u:DN%u:DR%u\n", + beiscsi_conn->beiscsi_conn_cid, err, + pasync_handle->is_header ? 'H' : 'D', + wq->hdr_len, wq->bytes_needed, + pasync_handle->buffer_len); + /* discard this handle */ + beiscsi_hdl_put_handle(pasync_ctx, pasync_handle); + /* free all the other handles in cri_wait_queue */ + beiscsi_hdl_purge_handles(phba, pasync_ctx, cri); + /* try continuing */ + return status; +} + +static void +beiscsi_hdq_post_handles(struct beiscsi_hba *phba, + u8 header, u8 ulp_num) +{ + struct hd_async_handle *pasync_handle, *tmp, **slot; + struct hd_async_context *pasync_ctx; struct hwi_controller *phwi_ctrlr; - struct hwi_async_pdu_context *pasync_ctx; - struct async_pdu_handle *pasync_handle; - struct list_head *pfree_link, *pbusy_list; + struct list_head *hfree_list; struct phys_addr *pasync_sge; - unsigned int ring_id, num_entries; - unsigned int host_write_num, doorbell_offset; - unsigned int writables; - unsigned int i = 0; - u32 doorbell = 0; + u32 ring_id, doorbell = 0; + u16 index, num_entries; + u32 doorbell_offset; + u16 prod = 0, cons; phwi_ctrlr = phba->phwi_ctrlr; pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num); num_entries = pasync_ctx->num_entries; - - if (is_header) { - writables = min(pasync_ctx->async_header.writables, - pasync_ctx->async_header.free_entries); - pfree_link = pasync_ctx->async_header.free_list.next; - host_write_num = pasync_ctx->async_header.host_write_ptr; + if (header) { + cons = pasync_ctx->async_header.free_entries; + hfree_list = &pasync_ctx->async_header.free_list; ring_id = phwi_ctrlr->default_pdu_hdr[ulp_num].id; doorbell_offset = phwi_ctrlr->default_pdu_hdr[ulp_num]. - doorbell_offset; + doorbell_offset; } else { - writables = min(pasync_ctx->async_data.writables, - pasync_ctx->async_data.free_entries); - pfree_link = pasync_ctx->async_data.free_list.next; - host_write_num = pasync_ctx->async_data.host_write_ptr; + cons = pasync_ctx->async_data.free_entries; + hfree_list = &pasync_ctx->async_data.free_list; ring_id = phwi_ctrlr->default_pdu_data[ulp_num].id; doorbell_offset = phwi_ctrlr->default_pdu_data[ulp_num]. - doorbell_offset; + doorbell_offset; } + /* number of entries posted must be in multiples of 8 */ + if (cons % 8) + return; - writables = (writables / 8) * 8; - if (writables) { - for (i = 0; i < writables; i++) { - pbusy_list = - hwi_get_async_busy_list(pasync_ctx, is_header, - host_write_num); - pasync_handle = - list_entry(pfree_link, struct async_pdu_handle, - link); - WARN_ON(!pasync_handle); - pasync_handle->consumed = 0; + list_for_each_entry_safe(pasync_handle, tmp, hfree_list, link) { + list_del_init(&pasync_handle->link); + pasync_handle->is_final = 0; + pasync_handle->buffer_len = 0; - pfree_link = pfree_link->next; - - pasync_sge = hwi_get_ring_address(pasync_ctx, - is_header, host_write_num); - - pasync_sge->hi = pasync_handle->pa.u.a32.address_lo; - pasync_sge->lo = pasync_handle->pa.u.a32.address_hi; - - list_move(&pasync_handle->link, pbusy_list); - - host_write_num++; - host_write_num = host_write_num % num_entries; + /* handles can be consumed out of order, use index in handle */ + index = pasync_handle->index; + WARN_ON(pasync_handle->is_header != header); + if (header) + slot = &pasync_ctx->async_entry[index].header; + else + slot = &pasync_ctx->async_entry[index].data; + /** + * The slot just tracks handle's hold and release, so + * overwriting at the same index won't do any harm but + * needs to be caught. + */ + if (*slot != NULL) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : async PDU %s slot at %u not empty\n", + header ? "header" : "data", index); } + /** + * We use same freed index as in completion to post so this + * operation is not required for refills. Its required only + * for ring creation. + */ + if (header) + pasync_sge = pasync_ctx->async_header.ring_base; + else + pasync_sge = pasync_ctx->async_data.ring_base; + pasync_sge += index; + /* if its a refill then address is same; hi is lo */ + WARN_ON(pasync_sge->hi && + pasync_sge->hi != pasync_handle->pa.u.a32.address_lo); + WARN_ON(pasync_sge->lo && + pasync_sge->lo != pasync_handle->pa.u.a32.address_hi); + pasync_sge->hi = pasync_handle->pa.u.a32.address_lo; + pasync_sge->lo = pasync_handle->pa.u.a32.address_hi; - if (is_header) { - pasync_ctx->async_header.host_write_ptr = - host_write_num; - pasync_ctx->async_header.free_entries -= writables; - pasync_ctx->async_header.writables -= writables; - pasync_ctx->async_header.busy_entries += writables; - } else { - pasync_ctx->async_data.host_write_ptr = host_write_num; - pasync_ctx->async_data.free_entries -= writables; - pasync_ctx->async_data.writables -= writables; - pasync_ctx->async_data.busy_entries += writables; - } - - doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK; - doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT; - doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT; - doorbell |= (writables & DB_DEF_PDU_CQPROC_MASK) - << DB_DEF_PDU_CQPROC_SHIFT; - - iowrite32(doorbell, phba->db_va + doorbell_offset); + *slot = pasync_handle; + if (++prod == cons) + break; } + if (header) + pasync_ctx->async_header.free_entries -= prod; + else + pasync_ctx->async_data.free_entries -= prod; + + doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK; + doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT; + doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT; + doorbell |= (prod & DB_DEF_PDU_CQPROC_MASK) << DB_DEF_PDU_CQPROC_SHIFT; + iowrite32(doorbell, phba->db_va + doorbell_offset); } -static void hwi_flush_default_pdu_buffer(struct beiscsi_hba *phba, - struct beiscsi_conn *beiscsi_conn, - struct i_t_dpdu_cqe *pdpdu_cqe) +static void +beiscsi_hdq_process_compl(struct beiscsi_conn *beiscsi_conn, + struct i_t_dpdu_cqe *pdpdu_cqe) { + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct hd_async_handle *pasync_handle = NULL; + struct hd_async_context *pasync_ctx; struct hwi_controller *phwi_ctrlr; - struct hwi_async_pdu_context *pasync_ctx; - struct async_pdu_handle *pasync_handle = NULL; - unsigned int cq_index = -1; - uint16_t cri_index = BE_GET_CRI_FROM_CID( - beiscsi_conn->beiscsi_conn_cid); + u16 cid_cri; + u8 ulp_num; phwi_ctrlr = phba->phwi_ctrlr; - pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, - BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, - cri_index)); + cid_cri = BE_GET_CRI_FROM_CID(beiscsi_conn->beiscsi_conn_cid); + ulp_num = BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, cid_cri); + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num); + pasync_handle = beiscsi_hdl_get_handle(beiscsi_conn, pasync_ctx, + pdpdu_cqe); + if (!pasync_handle) + return; - pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, - pdpdu_cqe, &cq_index); - BUG_ON(pasync_handle->is_header != 0); - if (pasync_handle->consumed == 0) - hwi_update_async_writables(phba, pasync_ctx, - pasync_handle->is_header, cq_index); - - hwi_free_async_msg(phba, pasync_ctx, pasync_handle->cri); - hwi_post_async_buffers(phba, pasync_handle->is_header, - BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, - cri_index)); -} - -static unsigned int -hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, - struct hwi_async_pdu_context *pasync_ctx, unsigned short cri) -{ - struct list_head *plist; - struct async_pdu_handle *pasync_handle; - void *phdr = NULL; - unsigned int hdr_len = 0, buf_len = 0; - unsigned int status, index = 0, offset = 0; - void *pfirst_buffer = NULL; - unsigned int num_buf = 0; - - plist = &pasync_ctx->async_entry[cri].wait_queue.list; - - list_for_each_entry(pasync_handle, plist, link) { - if (index == 0) { - phdr = pasync_handle->pbuffer; - hdr_len = pasync_handle->buffer_len; - } else { - buf_len = pasync_handle->buffer_len; - if (!num_buf) { - pfirst_buffer = pasync_handle->pbuffer; - num_buf++; - } - memcpy(pfirst_buffer + offset, - pasync_handle->pbuffer, buf_len); - offset += buf_len; - } - index++; - } - - status = beiscsi_process_async_pdu(beiscsi_conn, phba, - phdr, hdr_len, pfirst_buffer, - offset); - - hwi_free_async_msg(phba, pasync_ctx, cri); - return 0; -} - -static unsigned int -hwi_gather_async_pdu(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, - struct async_pdu_handle *pasync_handle) -{ - struct hwi_async_pdu_context *pasync_ctx; - struct hwi_controller *phwi_ctrlr; - unsigned int bytes_needed = 0, status = 0; - unsigned short cri = pasync_handle->cri; - struct pdu_base *ppdu; - - phwi_ctrlr = phba->phwi_ctrlr; - pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, - BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, - BE_GET_CRI_FROM_CID(beiscsi_conn-> - beiscsi_conn_cid))); - - list_del(&pasync_handle->link); - if (pasync_handle->is_header) { - pasync_ctx->async_header.busy_entries--; - if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) { - hwi_free_async_msg(phba, pasync_ctx, cri); - BUG(); - } - - pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; - pasync_ctx->async_entry[cri].wait_queue.hdr_received = 1; - pasync_ctx->async_entry[cri].wait_queue.hdr_len = - (unsigned short)pasync_handle->buffer_len; - list_add_tail(&pasync_handle->link, - &pasync_ctx->async_entry[cri].wait_queue.list); - - ppdu = pasync_handle->pbuffer; - bytes_needed = ((((ppdu->dw[offsetof(struct amap_pdu_base, - data_len_hi) / 32] & PDUBASE_DATALENHI_MASK) << 8) & - 0xFFFF0000) | ((be16_to_cpu((ppdu-> - dw[offsetof(struct amap_pdu_base, data_len_lo) / 32] - & PDUBASE_DATALENLO_MASK) >> 16)) & 0x0000FFFF)); - - if (status == 0) { - pasync_ctx->async_entry[cri].wait_queue.bytes_needed = - bytes_needed; - - if (bytes_needed == 0) - status = hwi_fwd_async_msg(beiscsi_conn, phba, - pasync_ctx, cri); - } - } else { - pasync_ctx->async_data.busy_entries--; - if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) { - list_add_tail(&pasync_handle->link, - &pasync_ctx->async_entry[cri].wait_queue. - list); - pasync_ctx->async_entry[cri].wait_queue. - bytes_received += - (unsigned short)pasync_handle->buffer_len; - - if (pasync_ctx->async_entry[cri].wait_queue. - bytes_received >= - pasync_ctx->async_entry[cri].wait_queue. - bytes_needed) - status = hwi_fwd_async_msg(beiscsi_conn, phba, - pasync_ctx, cri); - } - } - return status; -} - -static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, - struct i_t_dpdu_cqe *pdpdu_cqe) -{ - struct hwi_controller *phwi_ctrlr; - struct hwi_async_pdu_context *pasync_ctx; - struct async_pdu_handle *pasync_handle = NULL; - unsigned int cq_index = -1; - uint16_t cri_index = BE_GET_CRI_FROM_CID( - beiscsi_conn->beiscsi_conn_cid); - - phwi_ctrlr = phba->phwi_ctrlr; - pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, - BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, - cri_index)); - - pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, - pdpdu_cqe, &cq_index); - - if (pasync_handle->consumed == 0) - hwi_update_async_writables(phba, pasync_ctx, - pasync_handle->is_header, cq_index); - - hwi_gather_async_pdu(beiscsi_conn, phba, pasync_handle); - hwi_post_async_buffers(phba, pasync_handle->is_header, - BEISCSI_GET_ULP_FROM_CRI( - phwi_ctrlr, cri_index)); + beiscsi_hdl_gather_pdu(beiscsi_conn, pasync_ctx, pasync_handle); + beiscsi_hdq_post_handles(phba, pasync_handle->is_header, ulp_num); } void beiscsi_process_mcc_cq(struct beiscsi_hba *phba) @@ -2004,8 +1950,8 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) cqe_desc[code], code, cid); spin_lock_bh(&phba->async_pdu_lock); - hwi_process_default_pdu_ring(beiscsi_conn, phba, - (struct i_t_dpdu_cqe *)sol); + beiscsi_hdq_process_compl(beiscsi_conn, + (struct i_t_dpdu_cqe *)sol); spin_unlock_bh(&phba->async_pdu_lock); break; case UNSOL_DATA_NOTIFY: @@ -2015,8 +1961,8 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) cqe_desc[code], code, cid); spin_lock_bh(&phba->async_pdu_lock); - hwi_process_default_pdu_ring(beiscsi_conn, phba, - (struct i_t_dpdu_cqe *)sol); + beiscsi_hdq_process_compl(beiscsi_conn, + (struct i_t_dpdu_cqe *)sol); spin_unlock_bh(&phba->async_pdu_lock); break; case CXN_INVALIDATE_INDEX_NOTIFY: @@ -2052,8 +1998,9 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) "BM_%d : Dropping %s[%d] on DPDU ring on CID : %d\n", cqe_desc[code], code, cid); spin_lock_bh(&phba->async_pdu_lock); - hwi_flush_default_pdu_buffer(phba, beiscsi_conn, - (struct i_t_dpdu_cqe *) sol); + /* driver consumes the entry and drops the contents */ + beiscsi_hdq_process_compl(beiscsi_conn, + (struct i_t_dpdu_cqe *)sol); spin_unlock_bh(&phba->async_pdu_lock); break; case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL: @@ -2503,20 +2450,20 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba) (ulp_num * MEM_DESCR_OFFSET)); phba->mem_req[mem_descr_index] = BEISCSI_GET_CID_COUNT(phba, ulp_num) * - sizeof(struct async_pdu_handle); + sizeof(struct hd_async_handle); mem_descr_index = (HWI_MEM_ASYNC_DATA_HANDLE_ULP0 + (ulp_num * MEM_DESCR_OFFSET)); phba->mem_req[mem_descr_index] = BEISCSI_GET_CID_COUNT(phba, ulp_num) * - sizeof(struct async_pdu_handle); + sizeof(struct hd_async_handle); mem_descr_index = (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 + (ulp_num * MEM_DESCR_OFFSET)); phba->mem_req[mem_descr_index] = - sizeof(struct hwi_async_pdu_context) + + sizeof(struct hd_async_context) + (BEISCSI_GET_CID_COUNT(phba, ulp_num) * - sizeof(struct hwi_async_entry)); + sizeof(struct hd_async_entry)); } } } @@ -2775,35 +2722,34 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) uint8_t ulp_num; struct hwi_controller *phwi_ctrlr; struct hba_parameters *p = &phba->params; - struct hwi_async_pdu_context *pasync_ctx; - struct async_pdu_handle *pasync_header_h, *pasync_data_h; + struct hd_async_context *pasync_ctx; + struct hd_async_handle *pasync_header_h, *pasync_data_h; unsigned int index, idx, num_per_mem, num_async_data; struct be_mem_descriptor *mem_descr; for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { - + /* get async_ctx for each ULP */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 + (ulp_num * MEM_DESCR_OFFSET)); phwi_ctrlr = phba->phwi_ctrlr; phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num] = - (struct hwi_async_pdu_context *) + (struct hd_async_context *) mem_descr->mem_array[0].virtual_address; pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num]; memset(pasync_ctx, 0, sizeof(*pasync_ctx)); pasync_ctx->async_entry = - (struct hwi_async_entry *) + (struct hd_async_entry *) ((long unsigned int)pasync_ctx + - sizeof(struct hwi_async_pdu_context)); + sizeof(struct hd_async_context)); pasync_ctx->num_entries = BEISCSI_GET_CID_COUNT(phba, ulp_num); - pasync_ctx->buffer_size = p->defpdu_hdr_sz; - + /* setup header buffers */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_HEADER_BUF_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -2820,6 +2766,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) "BM_%d : No Virtual address for ULP : %d\n", ulp_num); + pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz; pasync_ctx->async_header.va_base = mem_descr->mem_array[0].virtual_address; @@ -2827,6 +2774,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) mem_descr->mem_array[0]. bus_address.u.a64.address; + /* setup header buffer sgls */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_HEADER_RING_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -2846,6 +2794,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) pasync_ctx->async_header.ring_base = mem_descr->mem_array[0].virtual_address; + /* setup header buffer handles */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -2864,9 +2813,9 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) pasync_ctx->async_header.handle_base = mem_descr->mem_array[0].virtual_address; - pasync_ctx->async_header.writables = 0; INIT_LIST_HEAD(&pasync_ctx->async_header.free_list); + /* setup data buffer sgls */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_DATA_RING_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -2886,6 +2835,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) pasync_ctx->async_data.ring_base = mem_descr->mem_array[0].virtual_address; + /* setup data buffer handles */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_DATA_HANDLE_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -2897,16 +2847,16 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) pasync_ctx->async_data.handle_base = mem_descr->mem_array[0].virtual_address; - pasync_ctx->async_data.writables = 0; INIT_LIST_HEAD(&pasync_ctx->async_data.free_list); pasync_header_h = - (struct async_pdu_handle *) + (struct hd_async_handle *) pasync_ctx->async_header.handle_base; pasync_data_h = - (struct async_pdu_handle *) + (struct hd_async_handle *) pasync_ctx->async_data.handle_base; + /* setup data buffers */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_DATA_BUF_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -2924,6 +2874,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ulp_num); idx = 0; + pasync_ctx->async_data.buffer_size = p->defpdu_data_sz; pasync_ctx->async_data.va_base = mem_descr->mem_array[idx].virtual_address; pasync_ctx->async_data.pa_base.u.a64.address = @@ -2937,7 +2888,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) for (index = 0; index < BEISCSI_GET_CID_COUNT (phba, ulp_num); index++) { pasync_header_h->cri = -1; - pasync_header_h->index = (char)index; + pasync_header_h->is_header = 1; + pasync_header_h->index = index; INIT_LIST_HEAD(&pasync_header_h->link); pasync_header_h->pbuffer = (void *)((unsigned long) @@ -2954,14 +2906,13 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) free_list); pasync_header_h++; pasync_ctx->async_header.free_entries++; - pasync_ctx->async_header.writables++; + INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. + wq.list); + pasync_ctx->async_entry[index].header = NULL; - INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. - wait_queue.list); - INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. - header_busy_list); pasync_data_h->cri = -1; - pasync_data_h->index = (char)index; + pasync_data_h->is_header = 0; + pasync_data_h->index = index; INIT_LIST_HEAD(&pasync_data_h->link); if (!num_async_data) { @@ -2996,16 +2947,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) free_list); pasync_data_h++; pasync_ctx->async_data.free_entries++; - pasync_ctx->async_data.writables++; - - INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. - data_busy_list); + pasync_ctx->async_entry[index].data = NULL; } - - pasync_ctx->async_header.host_write_ptr = 0; - pasync_ctx->async_header.ep_read_ptr = -1; - pasync_ctx->async_data.host_write_ptr = 0; - pasync_ctx->async_data.ep_read_ptr = -1; } } @@ -3707,7 +3650,7 @@ static void hwi_cleanup_port(struct beiscsi_hba *phba) struct be_ctrl_info *ctrl = &phba->ctrl; struct hwi_controller *phwi_ctrlr; struct hwi_context_memory *phwi_context; - struct hwi_async_pdu_context *pasync_ctx; + struct hd_async_context *pasync_ctx; int i, eq_for_mcc, ulp_num; for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) @@ -3820,7 +3763,6 @@ static int hwi_init_port(struct beiscsi_hba *phba) for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { - def_pdu_ring_sz = BEISCSI_GET_CID_COUNT(phba, ulp_num) * sizeof(struct phys_addr); @@ -3851,10 +3793,10 @@ static int hwi_init_port(struct beiscsi_hba *phba) * let EP know about it. * Call beiscsi_cmd_iscsi_cleanup before posting? */ - hwi_post_async_buffers(phba, BEISCSI_DEFQ_HDR, - ulp_num); - hwi_post_async_buffers(phba, BEISCSI_DEFQ_DATA, - ulp_num); + beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR, + ulp_num); + beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA, + ulp_num); } } @@ -3883,7 +3825,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { uint16_t cri = 0; - struct hwi_async_pdu_context *pasync_ctx; + struct hd_async_context *pasync_ctx; pasync_ctx = HWI_GET_ASYNC_PDU_CTX( phwi_ctrlr, ulp_num); @@ -3895,6 +3837,14 @@ static int hwi_init_port(struct beiscsi_hba *phba) phwi_ctrlr->wrb_context[cri].cid] = async_arr_idx++; } + /** + * Now that the default PDU rings have been created, + * let EP know about it. + */ + beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR, + ulp_num); + beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA, + ulp_num); } } diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 953d94a84f9c..e5e3154a8419 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -608,80 +608,81 @@ struct amap_beiscsi_offload_params { u8 max_recv_data_segment_length[32]; }; -/* void hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, struct sol_cqe *psol);*/ - -struct async_pdu_handle { +struct hd_async_handle { struct list_head link; struct be_bus_address pa; void *pbuffer; - unsigned int consumed; - unsigned char index; - unsigned char is_header; - unsigned short cri; - unsigned long buffer_len; + u32 buffer_len; + u16 index; + u16 cri; + u8 is_header; + u8 is_final; }; -struct hwi_async_entry { - struct { - unsigned char hdr_received; - unsigned char hdr_len; - unsigned short bytes_received; +/** + * This has list of async PDUs that are waiting to be processed. + * Buffers live in this list for a brief duration before they get + * processed and posted back to hardware. + * Note that we don't really need one cri_wait_queue per async_entry. + * We need one cri_wait_queue per CRI. Its easier to manage if this + * is tagged along with the async_entry. + */ +struct hd_async_entry { + struct cri_wait_queue { + unsigned short hdr_len; + unsigned int bytes_received; unsigned int bytes_needed; struct list_head list; - } wait_queue; - - struct list_head header_busy_list; - struct list_head data_busy_list; + } wq; + /* handles posted to FW resides here */ + struct hd_async_handle *header; + struct hd_async_handle *data; }; -struct hwi_async_pdu_context { - struct { - struct be_bus_address pa_base; - void *va_base; - void *ring_base; - struct async_pdu_handle *handle_base; +struct hd_async_buf_context { + struct be_bus_address pa_base; + void *va_base; + void *ring_base; + struct hd_async_handle *handle_base; + u16 free_entries; + u32 buffer_size; + /** + * Once iSCSI layer finishes processing an async PDU, the + * handles used for the PDU are added to this list. + * They are posted back to FW in groups of 8. + */ + struct list_head free_list; +}; - unsigned int host_write_ptr; - unsigned int ep_read_ptr; - unsigned int writables; - - unsigned int free_entries; - unsigned int busy_entries; - - struct list_head free_list; - } async_header; - - struct { - struct be_bus_address pa_base; - void *va_base; - void *ring_base; - struct async_pdu_handle *handle_base; - - unsigned int host_write_ptr; - unsigned int ep_read_ptr; - unsigned int writables; - - unsigned int free_entries; - unsigned int busy_entries; - struct list_head free_list; - } async_data; - - unsigned int buffer_size; - unsigned int num_entries; +/** + * hd_async_context is declared for each ULP supporting iSCSI function. + */ +struct hd_async_context { + struct hd_async_buf_context async_header; + struct hd_async_buf_context async_data; + u16 num_entries; + /** + * When unsol PDU is in, it needs to be chained till all the bytes are + * received and then processing is done. hd_async_entry is created + * based on the cid_count for each ULP. When unsol PDU comes in based + * on the conn_id it needs to be added to the correct async_entry wq. + * Below defined cid_to_async_cri_map is used to reterive the + * async_cri_map for a particular connection. + * + * This array is initialized after beiscsi_create_wrb_rings returns. + * + * - this method takes more memory space, fixed to 2K + * - any support for connections greater than this the array size needs + * to be incremented + */ #define BE_GET_ASYNC_CRI_FROM_CID(cid) (pasync_ctx->cid_to_async_cri_map[cid]) unsigned short cid_to_async_cri_map[BE_MAX_SESSION]; /** - * This is a varying size list! Do not add anything - * after this entry!! + * This is a variable size array. Don`t add anything after this field!! */ - struct hwi_async_entry *async_entry; + struct hd_async_entry *async_entry; }; -#define PDUCQE_CODE_MASK 0x0000003F -#define PDUCQE_DPL_MASK 0xFFFF0000 -#define PDUCQE_INDEX_MASK 0x0000FFFF - struct i_t_dpdu_cqe { u32 dw[4]; } __packed; @@ -1077,9 +1078,14 @@ struct hwi_context_memory { struct be_queue_info be_cq[MAX_CPUS - 1]; struct be_queue_info *be_wrbq; + /** + * Create array of ULP number for below entries as DEFQ + * will be created for both ULP if iSCSI Protocol is + * loaded on both ULP. + */ struct be_queue_info be_def_hdrq[BEISCSI_ULP_COUNT]; struct be_queue_info be_def_dataq[BEISCSI_ULP_COUNT]; - struct hwi_async_pdu_context *pasync_ctx[BEISCSI_ULP_COUNT]; + struct hd_async_context *pasync_ctx[BEISCSI_ULP_COUNT]; }; void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle); From e1f9d31eb3272b4351239ecb7481f541d9eb76be Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:22 +0530 Subject: [PATCH 053/173] scsi: be2iscsi: Fix bad WRB index error In very rare scenario, connection gets killed after throwing this error: scsi host0: BM_2312 : Event CXN_KILLED_BAD_WRB_INDEX_ERROR[15]... CID : 4 connection1:0: detected conn error (1011) memset ISCSI_WRB descriptor to zero for all allocations of WRB handle. Signed-off-by: Jitendra Bhivare Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_main.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 5024651e3a68..50d865eeb4c3 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -967,6 +967,7 @@ beiscsi_get_wrb_handle(struct hwi_wrb_context *pwrb_context, else pwrb_context->alloc_index++; spin_unlock_bh(&pwrb_context->wrb_lock); + memset(pwrb_handle->pwrb, 0, sizeof(*pwrb_handle->pwrb)); return pwrb_handle; } @@ -1209,11 +1210,10 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, struct beiscsi_hba *phba, struct sol_cqe *psol) { struct hwi_wrb_context *pwrb_context; - struct wrb_handle *pwrb_handle = NULL; - struct hwi_controller *phwi_ctrlr; - struct iscsi_task *task; - struct beiscsi_io_task *io_task; uint16_t wrb_index, cid, cri_index; + struct hwi_controller *phwi_ctrlr; + struct wrb_handle *pwrb_handle; + struct iscsi_task *task; phwi_ctrlr = phba->phwi_ctrlr; if (is_chip_be2_be3r(phba)) { @@ -1232,9 +1232,6 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, pwrb_context = &phwi_ctrlr->wrb_context[cri_index]; pwrb_handle = pwrb_context->pwrb_handle_basestd[wrb_index]; task = pwrb_handle->pio_handle; - - io_task = task->dd_data; - memset(io_task->pwrb_handle->pwrb, 0, sizeof(struct iscsi_wrb)); iscsi_put_task(task); } @@ -4295,16 +4292,12 @@ beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn, io_task = task->dd_data; if (io_task->pwrb_handle) { - memset(io_task->pwrb_handle->pwrb, 0, - sizeof(struct iscsi_wrb)); - free_wrb_handle(phba, pwrb_context, - io_task->pwrb_handle); + free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle); io_task->pwrb_handle = NULL; } if (io_task->psgl_handle) { - free_mgmt_sgl_handle(phba, - io_task->psgl_handle); + free_mgmt_sgl_handle(phba, io_task->psgl_handle); io_task->psgl_handle = NULL; } @@ -4341,6 +4334,7 @@ static void beiscsi_cleanup_task(struct iscsi_task *task) pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs, io_task->bhs_pa.u.a64.address); io_task->cmd_bhs = NULL; + task->hdr = NULL; } if (task->sc) { From 1b7a7ddceee5559a15c4c93020d7dcd9862babed Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:23 +0530 Subject: [PATCH 054/173] scsi: be2iscsi: Fix queue and connection parameters Current EQ delay is set to 0 to receive very high max interrupt per sec. Set EQ delay to 32 - reducing max interrupt rate from 65K to 20K per sec. Set TCP connection window size to 64K with scale shift count 2. Signed-off-by: Jitendra Bhivare Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_main.c | 2 +- drivers/scsi/be2iscsi/be_mgmt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 50d865eeb4c3..7c372b8e7dd5 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -3729,7 +3729,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) phwi_context = phwi_ctrlr->phwi_ctxt; phwi_context->max_eqd = 128; phwi_context->min_eqd = 0; - phwi_context->cur_eqd = 0; + phwi_context->cur_eqd = 32; /* set port optic state to unknown */ phba->optic_state = 0xff; diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 736eca38ea0b..adc827b17b91 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -338,7 +338,7 @@ int mgmt_open_connection(struct beiscsi_hba *phba, if (!is_chip_be2_be3r(phba)) { req->hdr.version = MBX_CMD_VER1; - req->tcp_window_size = 0; + req->tcp_window_size = 0x8000; req->tcp_window_scale_count = 2; } From 60f36e04bb7f580414fff9b6a9e2fe147a190c67 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:24 +0530 Subject: [PATCH 055/173] scsi: be2iscsi: Update copyright information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the copyright to: Copyright © xxxx - 2016 Broadcom Update email.ids: @avagotech.com - @broadcom.com Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be.h | 4 ++-- drivers/scsi/be2iscsi/be_cmds.c | 4 ++-- drivers/scsi/be2iscsi/be_cmds.h | 4 ++-- drivers/scsi/be2iscsi/be_iscsi.c | 6 +++--- drivers/scsi/be2iscsi/be_iscsi.h | 6 +++--- drivers/scsi/be2iscsi/be_main.c | 6 +++--- drivers/scsi/be2iscsi/be_main.h | 6 +++--- drivers/scsi/be2iscsi/be_mgmt.c | 6 +++--- drivers/scsi/be2iscsi/be_mgmt.h | 6 +++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h index bb4042c9acfb..b1d0fdc5d5e1 100644 --- a/drivers/scsi/be2iscsi/be.h +++ b/drivers/scsi/be2iscsi/be.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -8,7 +8,7 @@ * Public License is included in this distribution in the file called COPYING. * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 5fe161029585..be65da2988fb 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -8,7 +8,7 @@ * Public License is included in this distribution in the file called COPYING. * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index 8283344c3f93..328fb5b973cd 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -8,7 +8,7 @@ * Public License is included in this distribution in the file called COPYING. * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 35f7d3aab366..ba258217614e 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h index 2a4097a17ad0..e4d67dfea4cb 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.h +++ b/drivers/scsi/be2iscsi/be_iscsi.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Avago Technologies * 3333 Susan Street diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 7c372b8e7dd5..222edfceb507 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index e5e3154a8419..7173c035fbd8 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index adc827b17b91..3ac7b88afc57 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index ee6fd7e663cd..b897cfd57c72 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street From fa1a174f52c5b0c205c5a0bb970afe734a97e2e5 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:25 +0530 Subject: [PATCH 056/173] scsi: be2iscsi: Update the driver version Driver version: 11.2.0.0 Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 7173c035fbd8..6376657e45f7 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -36,7 +36,7 @@ #include #define DRV_NAME "be2iscsi" -#define BUILD_STR "11.0.0.0" +#define BUILD_STR "11.2.0.0" #define BE_NAME "Emulex OneConnect" \ "Open-iSCSI Driver version" BUILD_STR #define DRV_DESC BE_NAME " " "Driver" From 2a1e8447153259922c0d97eb5d35f76c9b41fbb0 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 19 Aug 2016 15:20:26 +0530 Subject: [PATCH 057/173] MAINTAINERS: Update be2iscsi contact info Signed-off-by: Jitendra Bhivare Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- MAINTAINERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5f502248ba7b..fec431c94002 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10562,12 +10562,12 @@ S: Maintained F: drivers/misc/phantom.c F: include/uapi/linux/phantom.h -SERVER ENGINES 10Gbps iSCSI - BladeEngine 2 DRIVER -M: Jayamohan Kallickal -M: Ketan Mukadam -M: John Soni Jose +Emulex 10Gbps iSCSI - OneConnect DRIVER +M: Subbu Seetharaman +M: Ketan Mukadam +M: Jitendra Bhivare L: linux-scsi@vger.kernel.org -W: http://www.avagotech.com +W: http://www.broadcom.com S: Supported F: drivers/scsi/be2iscsi/ From 709ab231f8b4417411147dd9730928ba214d5c31 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 21 Aug 2016 10:39:04 +0200 Subject: [PATCH 058/173] scsi: megaraid_sas: Use memdup_user() rather than duplicating its implementation Reuse existing functionality from memdup_user() instead of keeping duplicate source code. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked by: Sumit Saxena Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_base.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 2dab3dc2aa69..c87e5ef96ada 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -6711,14 +6711,9 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) unsigned long flags; u32 wait_time = MEGASAS_RESET_WAIT_TIME; - ioc = kmalloc(sizeof(*ioc), GFP_KERNEL); - if (!ioc) - return -ENOMEM; - - if (copy_from_user(ioc, user_ioc, sizeof(*ioc))) { - error = -EFAULT; - goto out_kfree_ioc; - } + ioc = memdup_user(user_ioc, sizeof(*ioc)); + if (IS_ERR(ioc)) + return PTR_ERR(ioc); instance = megasas_lookup_instance(ioc->host_no); if (!instance) { From 67f251759ea28a1e803deda47de6e37d4857ba69 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Sun, 21 Aug 2016 10:33:29 +0800 Subject: [PATCH 059/173] scsi: esas2r: not need to alloc zero buffer for local_atto_ioctl We don't need to use kzalloc as we will always memset the local_atto_ioctl later. Signed-off-by: Shawn Lin Acked-by: Bradley Grove Signed-off-by: Martin K. Petersen --- drivers/scsi/esas2r/esas2r_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c index 2aca4d16f39e..5092c821d088 100644 --- a/drivers/scsi/esas2r/esas2r_main.c +++ b/drivers/scsi/esas2r/esas2r_main.c @@ -194,7 +194,7 @@ static ssize_t write_hw(struct file *file, struct kobject *kobj, int length = min(sizeof(struct atto_ioctl), count); if (!a->local_atto_ioctl) { - a->local_atto_ioctl = kzalloc(sizeof(struct atto_ioctl), + a->local_atto_ioctl = kmalloc(sizeof(struct atto_ioctl), GFP_KERNEL); if (a->local_atto_ioctl == NULL) { esas2r_log(ESAS2R_LOG_WARN, From 108c8670df99dd689494c0ba981b5e82c863caf2 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Sun, 21 Aug 2016 10:39:24 +0800 Subject: [PATCH 060/173] scsi: esas2r: don't reinitialize adapter's req_table req_table is allocate by kzalloc, so we don't need to zero it again. Signed-off-by: Shawn Lin Acked-by: Bradley Grove Signed-off-by: Martin K. Petersen --- drivers/scsi/esas2r/esas2r_init.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c index 78ce4d61a69b..d6e53aee2295 100644 --- a/drivers/scsi/esas2r/esas2r_init.c +++ b/drivers/scsi/esas2r/esas2r_init.c @@ -963,10 +963,6 @@ bool esas2r_init_adapter_struct(struct esas2r_adapter *a, /* initialize the allocated memory */ if (test_bit(AF_FIRST_INIT, &a->flags)) { - memset(a->req_table, 0, - (num_requests + num_ae_requests + - 1) * sizeof(struct esas2r_request *)); - esas2r_targ_db_initialize(a); /* prime parts of the inbound list */ From 441c27401470c417cf4a33ab9c17bcefdf1ecca3 Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 24 Aug 2016 19:05:47 +0800 Subject: [PATCH 061/173] scsi: hisi_sas: add internal abort main code Add main code for internal abort functionality. The internal abort features allows the host controller to abort commands which are still active in the controller but have not yet been sent to the slave device. Typically a command only spends a relatively short time in the controller when compared to the amount of the time after it is sent to the slave device. Two modes of internal abort are supported: - device - individual command For device, when the internal abort is issued all commands in the host for that device are aborted. For a single command, only that command is aborted if it is still in the host. In HW the internal abort command is executed similar to any other sort of command, like SSP. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 8 ++ drivers/scsi/hisi_sas/hisi_sas_main.c | 163 ++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 4731d3241323..83113600721c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -56,6 +56,11 @@ enum dev_status { HISI_SAS_DEV_EH, }; +enum { + HISI_SAS_INT_ABT_CMD = 0, + HISI_SAS_INT_ABT_DEV = 1, +}; + enum hisi_sas_dev_type { HISI_SAS_DEV_TYPE_STP = 0, HISI_SAS_DEV_TYPE_SSP, @@ -146,6 +151,9 @@ struct hisi_sas_hw { struct hisi_sas_slot *slot); int (*prep_stp)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot); + int (*prep_abort)(struct hisi_hba *hisi_hba, + struct hisi_sas_slot *slot, + int device_id, int abort_flag, int tag_to_abort); int (*slot_complete)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int abort); void (*phy_enable)(struct hisi_hba *hisi_hba, int phy_no); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 18dd5ea2c721..763c6c58e4b9 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -17,6 +17,10 @@ static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device, u8 *lun, struct hisi_sas_tmf_task *tmf); +static int +hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, + struct domain_device *device, + int abort_flag, int tag); static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device) { @@ -116,6 +120,14 @@ static int hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba, return hisi_hba->hw->prep_stp(hisi_hba, slot); } +static int hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba, + struct hisi_sas_slot *slot, + int device_id, int abort_flag, int tag_to_abort) +{ + return hisi_hba->hw->prep_abort(hisi_hba, slot, + device_id, abort_flag, tag_to_abort); +} + /* * This function will issue an abort TMF regardless of whether the * task is in the sdev or not. Then it will do the task complete @@ -954,6 +966,157 @@ static int hisi_sas_query_task(struct sas_task *task) return rc; } +static int +hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, u64 device_id, + struct sas_task *task, int abort_flag, + int task_tag) +{ + struct domain_device *device = task->dev; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct device *dev = &hisi_hba->pdev->dev; + struct hisi_sas_port *port; + struct hisi_sas_slot *slot; + struct hisi_sas_cmd_hdr *cmd_hdr_base; + int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx; + + if (!device->port) + return -1; + + port = device->port->lldd_port; + + /* simply get a slot and send abort command */ + rc = hisi_sas_slot_index_alloc(hisi_hba, &slot_idx); + if (rc) + goto err_out; + rc = hisi_hba->hw->get_free_slot(hisi_hba, &dlvry_queue, + &dlvry_queue_slot); + if (rc) + goto err_out_tag; + + slot = &hisi_hba->slot_info[slot_idx]; + memset(slot, 0, sizeof(struct hisi_sas_slot)); + + slot->idx = slot_idx; + slot->n_elem = n_elem; + slot->dlvry_queue = dlvry_queue; + slot->dlvry_queue_slot = dlvry_queue_slot; + cmd_hdr_base = hisi_hba->cmd_hdr[dlvry_queue]; + slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot]; + slot->task = task; + slot->port = port; + task->lldd_task = slot; + + memset(slot->cmd_hdr, 0, sizeof(struct hisi_sas_cmd_hdr)); + + rc = hisi_sas_task_prep_abort(hisi_hba, slot, device_id, + abort_flag, task_tag); + if (rc) + goto err_out_tag; + + /* Port structure is static for the HBA, so + * even if the port is deformed it is ok + * to reference. + */ + list_add_tail(&slot->entry, &port->list); + spin_lock(&task->task_state_lock); + task->task_state_flags |= SAS_TASK_AT_INITIATOR; + spin_unlock(&task->task_state_lock); + + hisi_hba->slot_prep = slot; + + sas_dev->running_req++; + /* send abort command to our chip */ + hisi_hba->hw->start_delivery(hisi_hba); + + return 0; + +err_out_tag: + hisi_sas_slot_index_free(hisi_hba, slot_idx); +err_out: + dev_err(dev, "internal abort task prep: failed[%d]!\n", rc); + + return rc; +} + +/** + * hisi_sas_internal_task_abort -- execute an internal + * abort command for single IO command or a device + * @hisi_hba: host controller struct + * @device: domain device + * @abort_flag: mode of operation, device or single IO + * @tag: tag of IO to be aborted (only relevant to single + * IO mode) + */ +static int +hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, + struct domain_device *device, + int abort_flag, int tag) +{ + struct sas_task *task; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct device *dev = &hisi_hba->pdev->dev; + int res; + unsigned long flags; + + if (!hisi_hba->hw->prep_abort) + return -EOPNOTSUPP; + + task = sas_alloc_slow_task(GFP_KERNEL); + if (!task) + return -ENOMEM; + + task->dev = device; + task->task_proto = device->tproto; + task->task_done = hisi_sas_task_done; + task->slow_task->timer.data = (unsigned long)task; + task->slow_task->timer.function = hisi_sas_tmf_timedout; + task->slow_task->timer.expires = jiffies + 20*HZ; + add_timer(&task->slow_task->timer); + + /* Lock as we are alloc'ing a slot, which cannot be interrupted */ + spin_lock_irqsave(&hisi_hba->lock, flags); + res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id, + task, abort_flag, tag); + spin_unlock_irqrestore(&hisi_hba->lock, flags); + if (res) { + del_timer(&task->slow_task->timer); + dev_err(dev, "internal task abort: executing internal task failed: %d\n", + res); + goto exit; + } + wait_for_completion(&task->slow_task->completion); + res = TMF_RESP_FUNC_FAILED; + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == TMF_RESP_FUNC_COMPLETE) { + res = TMF_RESP_FUNC_COMPLETE; + goto exit; + } + + /* TMF timed out, return direct. */ + if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + dev_err(dev, "internal task abort: timeout.\n"); + if (task->lldd_task) { + struct hisi_sas_slot *slot = task->lldd_task; + + hisi_sas_slot_task_free(hisi_hba, task, slot); + } + } + } + +exit: + dev_info(dev, "internal task abort: task to dev %016llx task=%p " + "resp: 0x%x sts 0x%x\n", + SAS_ADDR(device->sas_addr), + task, + task->task_status.resp, /* 0 is complete, -1 is undelivered */ + task->task_status.stat); + sas_free_task(task); + + return res; +} + static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy) { hisi_sas_port_notify_formed(sas_phy); From 40f2702b57eb16ef918a0d328201043eb43438a3 Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 24 Aug 2016 19:05:48 +0800 Subject: [PATCH 062/173] scsi: hisi_sas: add internal abort in hisi_sas_dev_gone() Execute an internal abort for that device when it is removed, so that commands for that device are not processed. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 763c6c58e4b9..c4e59cd8ff7b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -621,6 +621,9 @@ static void hisi_sas_dev_gone(struct domain_device *device) dev_info(dev, "found dev[%lld:%x] is gone\n", sas_dev->device_id, sas_dev->dev_type); + hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + hisi_hba->hw->free_device(hisi_hba, sas_dev); device->lldd_dev = NULL; memset(sas_dev, 0, sizeof(*sas_dev)); From dc8a49cabc73dea7c1c05c6648dcfc67dbd0d5fc Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 24 Aug 2016 19:05:49 +0800 Subject: [PATCH 063/173] scsi: hisi_sas: add internal abort to hisi_sas_abort_task() Execute an internal abort for executing a task abort. This is for case of the command still being present in host when abort is executed. For a SATA internal abort, we set abort for all tasks associated with the device. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index c4e59cd8ff7b..280a19dae8eb 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -841,18 +841,22 @@ static int hisi_sas_abort_task(struct sas_task *task) } } + hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_CMD, tag); } else if (task->task_proto & SAS_PROTOCOL_SATA || task->task_proto & SAS_PROTOCOL_STP) { if (task->dev->dev_type == SAS_SATA_DEV) { - struct hisi_slot_info *slot = task->lldd_task; - - dev_notice(dev, "abort task: hba=%p task=%p slot=%p\n", - hisi_hba, task, slot); - task->task_state_flags |= SAS_TASK_STATE_ABORTED; + hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); rc = TMF_RESP_FUNC_COMPLETE; - goto out; } + } else if (task->task_proto & SAS_PROTOCOL_SMP) { + /* SMP */ + struct hisi_sas_slot *slot = task->lldd_task; + u32 tag = slot->idx; + hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_CMD, tag); } out: From a3e665d91fcafbf29c92b09e44dc29a9c1436a18 Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 24 Aug 2016 19:05:50 +0800 Subject: [PATCH 064/173] scsi: hisi_sas: add prep_abort_v2_hw() Add function to prepare the an internal abort for v2 hw. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index f96560431cf1..fec1675e572c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -174,6 +174,10 @@ /* HW dma structures */ /* Delivery queue header */ /* dw0 */ +#define CMD_HDR_ABORT_FLAG_OFF 0 +#define CMD_HDR_ABORT_FLAG_MSK (0x3 << CMD_HDR_ABORT_FLAG_OFF) +#define CMD_HDR_ABORT_DEVICE_TYPE_OFF 2 +#define CMD_HDR_ABORT_DEVICE_TYPE_MSK (0x1 << CMD_HDR_ABORT_DEVICE_TYPE_OFF) #define CMD_HDR_RESP_REPORT_OFF 5 #define CMD_HDR_RESP_REPORT_MSK (0x1 << CMD_HDR_RESP_REPORT_OFF) #define CMD_HDR_TLR_CTRL_OFF 6 @@ -214,6 +218,8 @@ #define CMD_HDR_DIF_SGL_LEN_MSK (0xffff << CMD_HDR_DIF_SGL_LEN_OFF) #define CMD_HDR_DATA_SGL_LEN_OFF 16 #define CMD_HDR_DATA_SGL_LEN_MSK (0xffff << CMD_HDR_DATA_SGL_LEN_OFF) +#define CMD_HDR_ABORT_IPTT_OFF 16 +#define CMD_HDR_ABORT_IPTT_MSK (0xffff << CMD_HDR_ABORT_IPTT_OFF) /* Completion header */ /* dw0 */ @@ -1775,6 +1781,32 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba, return 0; } +static int prep_abort_v2_hw(struct hisi_hba *hisi_hba, + struct hisi_sas_slot *slot, + int device_id, int abort_flag, int tag_to_abort) +{ + struct sas_task *task = slot->task; + struct domain_device *dev = task->dev; + struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; + struct hisi_sas_port *port = slot->port; + + /* dw0 */ + hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/ + (port->id << CMD_HDR_PORT_OFF) | + ((dev_is_sata(dev) ? 1:0) << + CMD_HDR_ABORT_DEVICE_TYPE_OFF) | + (abort_flag << CMD_HDR_ABORT_FLAG_OFF)); + + /* dw1 */ + hdr->dw1 = cpu_to_le32(device_id << CMD_HDR_DEV_ID_OFF); + + /* dw7 */ + hdr->dw7 = cpu_to_le32(tag_to_abort << CMD_HDR_ABORT_IPTT_OFF); + hdr->transfer_tags = cpu_to_le32(slot->idx); + + return 0; +} + static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) { int i, res = 0; @@ -2239,6 +2271,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = { .prep_smp = prep_smp_v2_hw, .prep_ssp = prep_ssp_v2_hw, .prep_stp = prep_ata_v2_hw, + .prep_abort = prep_abort_v2_hw, .get_free_slot = get_free_slot_v2_hw, .start_delivery = start_delivery_v2_hw, .slot_complete = slot_complete_v2_hw, From df032d0e4dbe2597dfa3f60c06b1c260e3870a3d Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 24 Aug 2016 19:05:51 +0800 Subject: [PATCH 065/173] scsi: hisi_sas: add v2 hw slot complete internal abort support Add code in slot_complete_v2_hw() to deal with the slots which have completed due to internal abort. The status codes have the following meaning: - STAT_IO_ABORTED: the IO has been aborted due to internal abort, whether by device or individual abort command - STAT_IO_COMPLETE: internal abort command has completed successfully for device or individual abort command - STAT_IO_NO_DEVICE: internal abort command has completed for device but cannot find any IO - STAT_IO_NOT_VALID: internal abort command has completed for single command but could not find the command Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index fec1675e572c..bf9b693a717f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -227,6 +227,13 @@ #define CMPLT_HDR_RSPNS_XFRD_MSK (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF) #define CMPLT_HDR_ERX_OFF 12 #define CMPLT_HDR_ERX_MSK (0x1 << CMPLT_HDR_ERX_OFF) +#define CMPLT_HDR_ABORT_STAT_OFF 13 +#define CMPLT_HDR_ABORT_STAT_MSK (0x7 << CMPLT_HDR_ABORT_STAT_OFF) +/* abort_stat */ +#define STAT_IO_NOT_VALID 0x1 +#define STAT_IO_NO_DEVICE 0x2 +#define STAT_IO_COMPLETE 0x3 +#define STAT_IO_ABORTED 0x4 /* dw1 */ #define CMPLT_HDR_IPTT_OFF 0 #define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF) @@ -1569,6 +1576,30 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, goto out; } + /* Use SAS+TMF status codes */ + switch ((complete_hdr->dw0 & CMPLT_HDR_ABORT_STAT_MSK) + >> CMPLT_HDR_ABORT_STAT_OFF) { + case STAT_IO_ABORTED: + /* this io has been aborted by abort command */ + ts->stat = SAS_ABORTED_TASK; + goto out; + case STAT_IO_COMPLETE: + /* internal abort command complete */ + ts->stat = TMF_RESP_FUNC_COMPLETE; + goto out; + case STAT_IO_NO_DEVICE: + ts->stat = TMF_RESP_FUNC_COMPLETE; + goto out; + case STAT_IO_NOT_VALID: + /* abort single io, controller don't find + * the io need to abort + */ + ts->stat = TMF_RESP_FUNC_FAILED; + goto out; + default: + break; + } + if ((complete_hdr->dw0 & CMPLT_HDR_ERX_MSK) && (!(complete_hdr->dw0 & CMPLT_HDR_RSPNS_XFRD_MSK))) { From 9859f24e9a25fc774c4d960568f12bdd634e00ca Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 24 Aug 2016 19:05:52 +0800 Subject: [PATCH 066/173] scsi: hisi_sas: fail tmf task prep when port detached When the port is detached we cannot execute a TMF, as there can be no device attached to the port. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 280a19dae8eb..73f581f7dc36 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -204,7 +204,7 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_hba *hisi_hba, return rc; } port = device->port->lldd_port; - if (port && !port->port_attached && !tmf) { + if (port && !port->port_attached) { if (sas_protocol_ata(task->task_proto)) { struct task_status_struct *ts = &task->task_status; From 4ffde482213ed77ea8743d1fb3eccad6e14b151b Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 24 Aug 2016 19:05:53 +0800 Subject: [PATCH 067/173] scsi: hisi_sas: add TMF success check When a tmf is issued, various response codes can be returned from the target. For a query tmf the response may be TMF_RESP_FUNC_COMPLETE or TMF_RESP_FUNC_SUCC. Add a condition for TMF_RESP_FUNC_SUCC to hisi_sas_exec_internal_tmf_task(). This affects query tmf, as the result is success the returned value was for failure. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 73f581f7dc36..85c73d311e4d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -743,6 +743,12 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, break; } + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == TMF_RESP_FUNC_SUCC) { + res = TMF_RESP_FUNC_SUCC; + break; + } + if (task->task_status.resp == SAS_TASK_COMPLETE && task->task_status.stat == SAS_DATA_UNDERRUN) { /* no error, but return the number of bytes of From 6328d9030f0fcf9935a00b1cd9d90c1129526a9c Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 24 Aug 2016 19:05:54 +0800 Subject: [PATCH 068/173] scsi: hisi_sas: update version to 1.6 Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 83113600721c..ca55ec2974e0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -23,7 +23,7 @@ #include #include -#define DRV_VERSION "v1.5" +#define DRV_VERSION "v1.6" #define HISI_SAS_MAX_PHYS 9 #define HISI_SAS_MAX_QUEUES 32 From afc3f83cb4a5bb3c8f18380bdeca6b65a6ff9543 Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 24 Aug 2016 12:56:51 -0500 Subject: [PATCH 069/173] scsi: ipr: Add asynchronous error notification This patch implements functions for pushing HCAM (host controlled asynchronous messages) error buffers to userspace through sysfs attributes. Reads to the "async_err_log" attribute will result in a single HCAM buffer being copied to userspace; one can process the next HCAM buffer by writing any string to the same attribute. A new list was added to the ioa_cfg structure to store the HCAM buffers for later reporting. We also send a KOBJ_CHANGE event whenever a new HCAM buffer is made available to userspace. Signed-off-by: Heitor Ricardo Alves de Siqueira Signed-off-by: Gabriel Krisman Bertazi Signed-off-by: Brian King Signed-off-by: Martin K. Petersen --- drivers/scsi/ipr.c | 125 ++++++++++++++++++++++++++++++++++++++++----- drivers/scsi/ipr.h | 7 ++- 2 files changed, 118 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index bf85974be862..5ecc32cecd10 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -1473,7 +1473,7 @@ static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd) struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc); - list_del(&hostrcb->queue); + list_del_init(&hostrcb->queue); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); if (ioasc) { @@ -2552,6 +2552,23 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg, } } +static struct ipr_hostrcb *ipr_get_free_hostrcb(struct ipr_ioa_cfg *ioa) +{ + struct ipr_hostrcb *hostrcb; + + hostrcb = list_first_entry_or_null(&ioa->hostrcb_free_q, + struct ipr_hostrcb, queue); + + if (unlikely(!hostrcb)) { + dev_info(&ioa->pdev->dev, "Reclaiming async error buffers."); + hostrcb = list_first_entry_or_null(&ioa->hostrcb_report_q, + struct ipr_hostrcb, queue); + } + + list_del_init(&hostrcb->queue); + return hostrcb; +} + /** * ipr_process_error - Op done function for an adapter error log. * @ipr_cmd: ipr command struct @@ -2569,13 +2586,14 @@ static void ipr_process_error(struct ipr_cmnd *ipr_cmd) struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc); u32 fd_ioasc; + char *envp[] = { "ASYNC_ERR_LOG=1", NULL }; if (ioa_cfg->sis64) fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error64.fd_ioasc); else fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error.fd_ioasc); - list_del(&hostrcb->queue); + list_del_init(&hostrcb->queue); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); if (!ioasc) { @@ -2588,6 +2606,10 @@ static void ipr_process_error(struct ipr_cmnd *ipr_cmd) "Host RCB failed with IOASC: 0x%08X\n", ioasc); } + list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_report_q); + hostrcb = ipr_get_free_hostrcb(ioa_cfg); + kobject_uevent_env(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE, envp); + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb); } @@ -4095,6 +4117,64 @@ static struct device_attribute ipr_ioa_fw_type_attr = { .show = ipr_show_fw_type }; +static ssize_t ipr_read_async_err_log(struct file *filep, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct device *cdev = container_of(kobj, struct device, kobj); + struct Scsi_Host *shost = class_to_shost(cdev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + struct ipr_hostrcb *hostrcb; + unsigned long lock_flags = 0; + int ret; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q, + struct ipr_hostrcb, queue); + if (!hostrcb) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return 0; + } + ret = memory_read_from_buffer(buf, count, &off, &hostrcb->hcam, + sizeof(hostrcb->hcam)); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return ret; +} + +static ssize_t ipr_next_async_err_log(struct file *filep, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct device *cdev = container_of(kobj, struct device, kobj); + struct Scsi_Host *shost = class_to_shost(cdev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + struct ipr_hostrcb *hostrcb; + unsigned long lock_flags = 0; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q, + struct ipr_hostrcb, queue); + if (!hostrcb) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return count; + } + + /* Reclaim hostrcb before exit */ + list_move_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return count; +} + +static struct bin_attribute ipr_ioa_async_err_log = { + .attr = { + .name = "async_err_log", + .mode = S_IRUGO | S_IWUSR, + }, + .size = 0, + .read = ipr_read_async_err_log, + .write = ipr_next_async_err_log +}; + static struct device_attribute *ipr_ioa_attrs[] = { &ipr_fw_version_attr, &ipr_log_level_attr, @@ -7026,8 +7106,7 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd) { struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; struct ipr_resource_entry *res; - struct ipr_hostrcb *hostrcb, *temp; - int i = 0, j; + int j; ENTER; ioa_cfg->in_reset_reload = 0; @@ -7048,12 +7127,16 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd) } schedule_work(&ioa_cfg->work_q); - list_for_each_entry_safe(hostrcb, temp, &ioa_cfg->hostrcb_free_q, queue) { - list_del(&hostrcb->queue); - if (i++ < IPR_NUM_LOG_HCAMS) - ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb); + for (j = 0; j < IPR_NUM_HCAMS; j++) { + list_del_init(&ioa_cfg->hostrcb[j]->queue); + if (j < IPR_NUM_LOG_HCAMS) + ipr_send_hcam(ioa_cfg, + IPR_HCAM_CDB_OP_CODE_LOG_DATA, + ioa_cfg->hostrcb[j]); else - ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); + ipr_send_hcam(ioa_cfg, + IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, + ioa_cfg->hostrcb[j]); } scsi_report_bus_reset(ioa_cfg->host, IPR_VSET_BUS); @@ -8335,7 +8418,7 @@ static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg) hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next, struct ipr_hostrcb, queue); - list_del(&hostrcb->queue); + list_del_init(&hostrcb->queue); memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam)); rc = ipr_get_ldump_data_section(ioa_cfg, @@ -9332,7 +9415,7 @@ static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg) dma_free_coherent(&ioa_cfg->pdev->dev, ioa_cfg->cfg_table_size, ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma); - for (i = 0; i < IPR_NUM_HCAMS; i++) { + for (i = 0; i < IPR_MAX_HCAMS; i++) { dma_free_coherent(&ioa_cfg->pdev->dev, sizeof(struct ipr_hostrcb), ioa_cfg->hostrcb[i], @@ -9572,7 +9655,7 @@ static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) if (!ioa_cfg->u.cfg_table) goto out_free_host_rrq; - for (i = 0; i < IPR_NUM_HCAMS; i++) { + for (i = 0; i < IPR_MAX_HCAMS; i++) { ioa_cfg->hostrcb[i] = dma_alloc_coherent(&pdev->dev, sizeof(struct ipr_hostrcb), &ioa_cfg->hostrcb_dma[i], @@ -9714,6 +9797,7 @@ static void ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg, INIT_LIST_HEAD(&ioa_cfg->hostrcb_free_q); INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q); + INIT_LIST_HEAD(&ioa_cfg->hostrcb_report_q); INIT_LIST_HEAD(&ioa_cfg->free_res_q); INIT_LIST_HEAD(&ioa_cfg->used_res_q); INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread); @@ -10352,6 +10436,8 @@ static void ipr_remove(struct pci_dev *pdev) &ipr_trace_attr); ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj, &ipr_dump_attr); + sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_ioa_async_err_log); scsi_remove_host(ioa_cfg->host); __ipr_remove(pdev); @@ -10400,10 +10486,25 @@ static int ipr_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) return rc; } + rc = sysfs_create_bin_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_ioa_async_err_log); + + if (rc) { + ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_dump_attr); + ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_trace_attr); + scsi_remove_host(ioa_cfg->host); + __ipr_remove(pdev); + return rc; + } + rc = ipr_create_dump_file(&ioa_cfg->host->shost_dev.kobj, &ipr_dump_attr); if (rc) { + sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_ioa_async_err_log); ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj, &ipr_trace_attr); scsi_remove_host(ioa_cfg->host); diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index cdb51960b53c..4dbeaafa0ba2 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -154,7 +154,9 @@ #define IPR_DEFAULT_MAX_ERROR_DUMP 984 #define IPR_NUM_LOG_HCAMS 2 #define IPR_NUM_CFG_CHG_HCAMS 2 +#define IPR_NUM_HCAM_QUEUE 12 #define IPR_NUM_HCAMS (IPR_NUM_LOG_HCAMS + IPR_NUM_CFG_CHG_HCAMS) +#define IPR_MAX_HCAMS (IPR_NUM_HCAMS + IPR_NUM_HCAM_QUEUE) #define IPR_MAX_SIS64_TARGETS_PER_BUS 1024 #define IPR_MAX_SIS64_LUNS_PER_TARGET 0xffffffff @@ -1532,10 +1534,11 @@ struct ipr_ioa_cfg { char ipr_hcam_label[8]; #define IPR_HCAM_LABEL "hcams" - struct ipr_hostrcb *hostrcb[IPR_NUM_HCAMS]; - dma_addr_t hostrcb_dma[IPR_NUM_HCAMS]; + struct ipr_hostrcb *hostrcb[IPR_MAX_HCAMS]; + dma_addr_t hostrcb_dma[IPR_MAX_HCAMS]; struct list_head hostrcb_free_q; struct list_head hostrcb_pending_q; + struct list_head hostrcb_report_q; struct ipr_hrr_queue hrrq[IPR_MAX_HRRQ_NUM]; u32 hrrq_num; From 0d5644b7d8daa3c1d91acb4367731f568c9c9469 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 3 Aug 2016 21:49:03 +0200 Subject: [PATCH 070/173] scsi: configure runtime pm before calling device_add in scsi_add_host_with_dma Runtime PM should be configured already once we call device_add. See also the description in this mail thread https://lists.linuxfoundation.org/pipermail/linux-pm/2009-November/023198.html or the order of calls e.g. in usb_new_device. The changed order also helps to avoid scenarios where runtime pm for &shost->shost_gendev is activated whilst the parent is suspended, resulting in error message "runtime PM trying to activate child device hostx but parent yyy is not active". In addition properly reverse the runtime pm calls in the error path. Signed-off-by: Heiner Kallweit Acked-by: Alan Stern Signed-off-by: Martin K. Petersen --- drivers/scsi/hosts.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index ba9af4a2bd2a..9ab94adbddb7 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -246,10 +246,6 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, shost->dma_dev = dma_dev; - error = device_add(&shost->shost_gendev); - if (error) - goto out_destroy_freelist; - /* * Increase usage count temporarily here so that calling * scsi_autopm_put_host() will trigger runtime idle if there is @@ -260,6 +256,10 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, pm_runtime_enable(&shost->shost_gendev); device_enable_async_suspend(&shost->shost_gendev); + error = device_add(&shost->shost_gendev); + if (error) + goto out_destroy_freelist; + scsi_host_set_state(shost, SHOST_RUNNING); get_device(shost->shost_gendev.parent); @@ -309,6 +309,10 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, out_del_gendev: device_del(&shost->shost_gendev); out_destroy_freelist: + device_disable_async_suspend(&shost->shost_gendev); + pm_runtime_disable(&shost->shost_gendev); + pm_runtime_set_suspended(&shost->shost_gendev); + pm_runtime_put_noidle(&shost->shost_gendev); scsi_destroy_command_freelist(shost); out_destroy_tags: if (shost_use_blk_mq(shost)) From 39f275740483141cc598cebeaada76581a615c6b Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 9 Aug 2016 13:30:02 +0200 Subject: [PATCH 071/173] scsi: aic94xx: Add missing error code assignment before test It is likely that checking the result of 'pci_write_config_dword' is expected here. Signed-off-by: Christophe JAILLET Signed-off-by: Martin K. Petersen --- drivers/scsi/aic94xx/aic94xx_hwi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index 0fdc98bc2338..7c713f797535 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -632,7 +632,7 @@ int asd_init_hw(struct asd_ha_struct *asd_ha) pci_name(asd_ha->pcidev)); return err; } - pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, + err = pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, v | SC_TMR_DIS); if (err) { asd_printk("couldn't disable split completion timer of %s\n", From bb5a505407dfbfbdfa9d6e18fbe917ac83ab023a Mon Sep 17 00:00:00 2001 From: Tyrel Datwyler Date: Wed, 3 Aug 2016 16:36:52 -0500 Subject: [PATCH 072/173] scsi: ibmvfc: Set READ FCP_XFER_READY DISABLED bit in PRLI The READ FCP_XFER_READY DISABLED bit is required to always be set to one since FCP-3. Set it in the service parameter page frame during process login. Signed-off-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi/ibmvfc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index ab67ec4b6bd6..4a680ce774ca 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -3381,6 +3381,7 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt) prli->parms.type = IBMVFC_SCSI_FCP_TYPE; prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR); prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC); + prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED); ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); if (ibmvfc_send_event(evt, vhost, default_timeout)) { From a6104b1e1846273d52b9230d700939fef0a9da80 Mon Sep 17 00:00:00 2001 From: Tyrel Datwyler Date: Wed, 3 Aug 2016 16:36:53 -0500 Subject: [PATCH 073/173] scsi: ibmvfc: add FC Class 3 Error Recovery support The ibmvfc driver currently doesn't support FC Class 3 Error Recovery. However, it is simply a matter of informing the VIOS that the payload expects to use sequence level error recovery via a bit flag in the ibmvfc_cmd structure. This patch adds a module parameter to enable error recovery support at boot time. When enabled the RETRY service parameter bit is set during PRLI, and ibmvfc_cmd->flags includes the IBMVFC_CLASS_3_ERR bit. Signed-off-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi/ibmvfc.c | 10 ++++++++++ drivers/scsi/ibmvscsi/ibmvfc.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 4a680ce774ca..6b92169abaeb 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -52,6 +52,7 @@ static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT; static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS; static unsigned int ibmvfc_debug = IBMVFC_DEBUG; static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL; +static unsigned int cls3_error = IBMVFC_CLS3_ERROR; static LIST_HEAD(ibmvfc_head); static DEFINE_SPINLOCK(ibmvfc_driver_lock); static struct scsi_transport_template *ibmvfc_transport_template; @@ -86,6 +87,9 @@ MODULE_PARM_DESC(debug, "Enable driver debug information. " module_param_named(log_level, log_level, uint, 0); MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. " "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]"); +module_param_named(cls3_error, cls3_error, uint, 0); +MODULE_PARM_DESC(log_level, "Enable FC Class 3 Error Recovery. " + "[Default=" __stringify(IBMVFC_CLS3_ERROR) "]"); static const struct { u16 status; @@ -1335,6 +1339,9 @@ static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd, struct srp_direct_buf *data = &vfc_cmd->ioba; struct ibmvfc_host *vhost = dev_get_drvdata(dev); + if (cls3_error) + vfc_cmd->flags |= cpu_to_be16(IBMVFC_CLASS_3_ERR); + sg_mapped = scsi_dma_map(scmd); if (!sg_mapped) { vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC); @@ -3383,6 +3390,9 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt) prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC); prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED); + if (cls3_error) + prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY); + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); if (ibmvfc_send_event(evt, vhost, default_timeout)) { vhost->discovery_threads--; diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index 5c70a52ad346..9a0696f68f37 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -54,6 +54,7 @@ #define IBMVFC_DEV_LOSS_TMO (5 * 60) #define IBMVFC_DEFAULT_LOG_LEVEL 2 #define IBMVFC_MAX_CDB_LEN 16 +#define IBMVFC_CLS3_ERROR 0 /* * Ensure we have resources for ERP and initialization: From b9b6e80ad3b11778f1066d698a838bcc86efb6be Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Fri, 19 Aug 2016 17:43:56 +0100 Subject: [PATCH 074/173] scsi: sg: Avoid overflow when USER_HZ > HZ Calculating the maximum timeout that a user can set via the SG_SET_TIMEOUT ioctl involves multiplying INT_MAX by USER_HZ/HZ. If USER_HZ is larger than HZ then this results in an overflow when performed as a 32 bit integer calculation, resulting in compiler warnings such as the following: drivers/scsi/sg.c: In function 'sg_ioctl': drivers/scsi/sg.c:91:67: warning: integer overflow in expression [-Woverflow] #define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL)) ^ drivers/scsi/sg.c:887:14: note: in expansion of macro 'MULDIV' if (val >= MULDIV (INT_MAX, USER_HZ, HZ)) ^ drivers/scsi/sg.c:91:67: warning: integer overflow in expression [-Woverflow] #define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL)) ^ drivers/scsi/sg.c:888:13: note: in expansion of macro 'MULDIV' val = MULDIV (INT_MAX, USER_HZ, HZ); ^ Avoid this overflow by performing the (constant) arithmetic on 64 bit integers, which ensures that overflow from multiplying the 32 bit values cannot occur. When converting the result back to a 32 bit integer use min_t to ensure that we don't simply truncate a value beyond INT_MAX to a 32 bit integer, but instead use INT_MAX where the result was larger than it. As the values are all compile time constant the 64 bit arithmetic should have no runtime cost. Signed-off-by: Paul Burton Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/sg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ae7d9bdf409c..bb5ec2d6abdf 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -884,8 +884,9 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return result; if (val < 0) return -EIO; - if (val >= MULDIV (INT_MAX, USER_HZ, HZ)) - val = MULDIV (INT_MAX, USER_HZ, HZ); + if (val >= MULDIV((s64)INT_MAX, USER_HZ, HZ)) + val = min_t(s64, MULDIV((s64)INT_MAX, USER_HZ, HZ), + INT_MAX); sfp->timeout_user = val; sfp->timeout = MULDIV (val, HZ, USER_HZ); From f8630bd7e2185d175f76b38b36609c45144e2d15 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Fri, 19 Aug 2016 17:43:57 +0100 Subject: [PATCH 075/173] scsi: sg: Use mult_frac, drop MULDIV macro The MULDIV macro is essentially a duplicate of the more standard mult_frac macro. Replace use of MULDIV with mult_frac & drop the duplication. Signed-off-by: Paul Burton Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/sg.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index bb5ec2d6abdf..070332eb41f3 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -79,18 +79,7 @@ static void sg_proc_cleanup(void); */ #define SG_MAX_CDB_SIZE 252 -/* - * Suppose you want to calculate the formula muldiv(x,m,d)=int(x * m / d) - * Then when using 32 bit integers x * m may overflow during the calculation. - * Replacing muldiv(x) by muldiv(x)=((x % d) * m) / d + int(x / d) * m - * calculates the same, but prevents the overflow when both m and d - * are "small" numbers (like HZ and USER_HZ). - * Of course an overflow is inavoidable if the result of muldiv doesn't fit - * in 32 bits. - */ -#define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL)) - -#define SG_DEFAULT_TIMEOUT MULDIV(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) +#define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) int sg_big_buff = SG_DEF_RESERVED_SIZE; /* N.B. This variable is readable and writeable via @@ -884,11 +873,11 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return result; if (val < 0) return -EIO; - if (val >= MULDIV((s64)INT_MAX, USER_HZ, HZ)) - val = min_t(s64, MULDIV((s64)INT_MAX, USER_HZ, HZ), + if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ)) + val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ), INT_MAX); sfp->timeout_user = val; - sfp->timeout = MULDIV (val, HZ, USER_HZ); + sfp->timeout = mult_frac(val, HZ, USER_HZ); return 0; case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ From 51f9039ffd751dee54fe26609df645bfd128b1d0 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 21 Aug 2016 10:28:25 +0200 Subject: [PATCH 076/173] scsi: megaraid_sas: Fix the search of first memory bar The 2nd parameter of 'find_first_bit' is the number of bits to search. In this case, we are passing 'sizeof(unsigned long)' which is likely to be 4. It is likely that the number of bits in a long was expected here, so use BITS_PER_LONG instead. Signed-off-by: Christophe JAILLET Acked-by: Sumit Saxena Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index c87e5ef96ada..7edc9e21c536 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -5036,7 +5036,7 @@ static int megasas_init_fw(struct megasas_instance *instance) /* Find first memory bar */ bar_list = pci_select_bars(instance->pdev, IORESOURCE_MEM); - instance->bar = find_first_bit(&bar_list, sizeof(unsigned long)); + instance->bar = find_first_bit(&bar_list, BITS_PER_LONG); if (pci_request_selected_regions(instance->pdev, instance->bar, "megasas: LSI")) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "IO memory region busy!\n"); From deeea8edc746c68d7aa2282322b14be701ec11ee Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 26 Aug 2016 15:09:06 +0530 Subject: [PATCH 077/173] scsi: be2iscsi: Fix error return code We know that 'ret' is not an error code because it has been tested a few lines above. So, if one of these function fails, 0 will be returned instead of an error code. Return -ENOMEM instead. Signed-off-by: Christophe JAILLET 'ret' needs to be set with error code if hba_setup_cid_tbls fails. Signed-off-by: Jitendra Bhivare Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_main.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 222edfceb507..b30db877972c 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -3017,8 +3017,8 @@ static int be_fill_queue(struct be_queue_info *q, static int beiscsi_create_eqs(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context) { + int ret = -ENOMEM, eq_for_mcc; unsigned int i, num_eq_pages; - int ret = 0, eq_for_mcc; struct be_queue_info *eq; struct be_dma_mem *mem; void *eq_vaddress; @@ -3036,8 +3036,8 @@ static int beiscsi_create_eqs(struct beiscsi_hba *phba, mem = &eq->dma_mem; phwi_context->be_eq[i].phba = phba; eq_vaddress = pci_alloc_consistent(phba->pcidev, - num_eq_pages * PAGE_SIZE, - &paddr); + num_eq_pages * PAGE_SIZE, + &paddr); if (!eq_vaddress) goto create_eq_error; @@ -3065,6 +3065,7 @@ static int beiscsi_create_eqs(struct beiscsi_hba *phba, phwi_context->be_eq[i].q.id); } return 0; + create_eq_error: for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) { eq = &phwi_context->be_eq[i].q; @@ -3081,11 +3082,11 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context) { unsigned int i, num_cq_pages; - int ret = 0; struct be_queue_info *cq, *eq; struct be_dma_mem *mem; struct be_eq_obj *pbe_eq; void *cq_vaddress; + int ret = -ENOMEM; dma_addr_t paddr; num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \ @@ -3099,10 +3100,11 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba, pbe_eq->phba = phba; mem = &cq->dma_mem; cq_vaddress = pci_alloc_consistent(phba->pcidev, - num_cq_pages * PAGE_SIZE, - &paddr); + num_cq_pages * PAGE_SIZE, + &paddr); if (!cq_vaddress) goto create_cq_error; + ret = be_fill_queue(cq, phba->params.num_cq_entries, sizeof(struct sol_cqe), cq_vaddress); if (ret) { @@ -3137,7 +3139,6 @@ create_cq_error: mem->va, mem->dma); } return ret; - } static int @@ -4230,7 +4231,8 @@ static int beiscsi_init_port(struct beiscsi_hba *phba) goto do_cleanup_ctrlr; } - if (hba_setup_cid_tbls(phba)) { + ret = hba_setup_cid_tbls(phba); + if (ret < 0) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Failed in hba_setup_cid_tbls\n"); kfree(phba->io_sgl_hndl_base); @@ -5627,7 +5629,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, struct hwi_context_memory *phwi_context; struct be_eq_obj *pbe_eq; unsigned int s_handle; - int ret = 0, i; + int ret, i; ret = beiscsi_enable_pci(pcidev); if (ret < 0) { @@ -5640,6 +5642,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, if (!phba) { dev_err(&pcidev->dev, "beiscsi_dev_probe - Failed in beiscsi_hba_alloc\n"); + ret = -ENOMEM; goto disable_pci; } @@ -5744,6 +5747,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : beiscsi_dev_probe-" "Failed to allocate work queue\n"); + ret = -ENOMEM; goto free_twq; } From bf9b7554f46c4736d72e61f076a2d91a4303d2ea Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 26 Aug 2016 15:09:07 +0530 Subject: [PATCH 078/173] scsi: be2iscsi: Remove redundant iscsi_wrb desc memset alloc_wrb_handle already does memset zero of iscsi_wrb descriptor so remove redundant memset in WRB submission paths. Add pwrb_handle NULL check before memsett'ing pwrb. Signed-off-by: Jitendra Bhivare Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_main.c | 5 +++-- drivers/scsi/be2iscsi/be_mgmt.c | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index b30db877972c..6a6906f847db 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -967,7 +967,9 @@ beiscsi_get_wrb_handle(struct hwi_wrb_context *pwrb_context, else pwrb_context->alloc_index++; spin_unlock_bh(&pwrb_context->wrb_lock); - memset(pwrb_handle->pwrb, 0, sizeof(*pwrb_handle->pwrb)); + + if (pwrb_handle) + memset(pwrb_handle->pwrb, 0, sizeof(*pwrb_handle->pwrb)); return pwrb_handle; } @@ -4718,7 +4720,6 @@ static int beiscsi_mtask(struct iscsi_task *task) cid = beiscsi_conn->beiscsi_conn_cid; pwrb = io_task->pwrb_handle->pwrb; - memset(pwrb, 0, sizeof(*pwrb)); if (is_chip_be2_be3r(phba)) { AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 3ac7b88afc57..b9ff9391dda7 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -1350,7 +1350,6 @@ void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params, { struct iscsi_wrb *pwrb = pwrb_handle->pwrb; - memset(pwrb, 0, sizeof(*pwrb)); AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, max_send_data_segment_length, pwrb, params->dw[offsetof(struct amap_beiscsi_offload_params, @@ -1422,8 +1421,6 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, { struct iscsi_wrb *pwrb = pwrb_handle->pwrb; - memset(pwrb, 0, sizeof(*pwrb)); - AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, max_burst_length, pwrb, params->dw[offsetof (struct amap_beiscsi_offload_params, From 658f18d1b82b9f9d89f7c74cd2bcbc9b33a74870 Mon Sep 17 00:00:00 2001 From: Jitendra Bhivare Date: Fri, 26 Aug 2016 15:09:08 +0530 Subject: [PATCH 079/173] scsi: be2iscsi: Add missing unlock for mbox_lock Julia pointed out beiscsi_boot_get_sinfo does not unlock mbox_lock on nonemb_cmd memory allocation failure. Signed-off-by: Jitendra Bhivare Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/be2iscsi/be_mgmt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index b9ff9391dda7..aebc4ddb3060 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -1085,8 +1085,10 @@ unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba) nonemb_cmd->va = pci_alloc_consistent(phba->ctrl.pdev, sizeof(nonemb_cmd->size), &nonemb_cmd->dma); - if (!nonemb_cmd->va) + if (!nonemb_cmd->va) { + mutex_unlock(&ctrl->mbox_lock); return 0; + } req = nonemb_cmd->va; memset(req, 0, sizeof(*req)); From 980b3271d15992c158a86d8ed8776ec9d89f3dd5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 22 Jul 2016 14:03:46 +0000 Subject: [PATCH 080/173] scsi: ibmvscsis: Use list_move_tail instead of list_del/list_add_tail Using list_move_tail() instead of list_del() + list_add_tail(). Signed-off-by: Wei Yongjun Reviewed-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index b29fef9d0f27..7e873a7635ec 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -3697,8 +3697,7 @@ static void ibmvscsis_release_cmd(struct se_cmd *se_cmd) spin_lock_bh(&vscsi->intr_lock); /* Remove from active_q */ - list_del(&cmd->list); - list_add_tail(&cmd->list, &vscsi->waiting_rsp); + list_move_tail(&cmd->list, &vscsi->waiting_rsp); ibmvscsis_send_messages(vscsi); spin_unlock_bh(&vscsi->intr_lock); } From 35d0acbaee5bb29e29d0b39e2853ec68d38806b8 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sat, 27 Aug 2016 12:29:59 +1000 Subject: [PATCH 081/173] scsi: Documentation/scsi: Remove nodisconnect parameter The driver that used the 'nodisconnect' parameter was removed in commit 565bae6a4a8f ("[SCSI] 53c7xx: kill driver"). Related documentation was cleaned up in commit f37a7238d379 ("[SCSI] 53c7xx: fix removal fallout"), except for the remaining two mentions that are removed here. Signed-off-by: Finn Thain Reviewed-by: Geert Uytterhoeven Signed-off-by: Martin K. Petersen --- Documentation/kernel-parameters.txt | 2 -- Documentation/scsi/scsi-parameters.txt | 2 -- 2 files changed, 4 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 46c030a49186..feb4b583a18a 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2571,8 +2571,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted. nodelayacct [KNL] Disable per-task delay accounting - nodisconnect [HW,SCSI,M68K] Disables SCSI disconnects. - nodsp [SH] Disable hardware DSP at boot time. noefi Disable EFI runtime services support. diff --git a/Documentation/scsi/scsi-parameters.txt b/Documentation/scsi/scsi-parameters.txt index 1241ac11edb1..d5ae6ced6be3 100644 --- a/Documentation/scsi/scsi-parameters.txt +++ b/Documentation/scsi/scsi-parameters.txt @@ -79,8 +79,6 @@ parameters may be changed at runtime by the command ncr53c8xx= [HW,SCSI] - nodisconnect [HW,SCSI,M68K] Disables SCSI disconnects. - osst= [HW,SCSI] SCSI Tape Driver Format: , See also Documentation/scsi/st.txt. From 01f17641c4c6e7468bbd3d3f382c151db01dc0e8 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sat, 27 Aug 2016 12:30:00 +1000 Subject: [PATCH 082/173] scsi: ncr5380: Avoid a compiler warning With commit 3a0f64bfa907 ("mac_scsi: Fix pseudo DMA implementation") some versions of gcc now warn: In file included from drivers/scsi/mac_scsi.c:335: drivers/scsi/NCR5380.h:295: warning: `NCR5380_poll_politely' declared inline after being called drivers/scsi/NCR5380.h:295: warning: previous declaration of `NCR5380_poll_politely' was here Avoid this by defining NCR5380_poll_politely() in NCR5380.h. [mkp: checkpatch warnings] Suggested-by: Geert Uytterhoeven Signed-off-by: Finn Thain Tested-by: Geert Uytterhoeven Signed-off-by: Martin K. Petersen --- drivers/scsi/NCR5380.c | 7 ------- drivers/scsi/NCR5380.h | 8 +++++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index 43908bbb3b23..b58c6a38fc32 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -230,13 +230,6 @@ static int NCR5380_poll_politely2(struct Scsi_Host *instance, return -ETIMEDOUT; } -static inline int NCR5380_poll_politely(struct Scsi_Host *instance, - int reg, int bit, int val, int wait) -{ - return NCR5380_poll_politely2(instance, reg, bit, val, - reg, bit, val, wait); -} - #if NDEBUG static struct { unsigned char mask; diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h index c60728785d89..2ed61b5d40e6 100644 --- a/drivers/scsi/NCR5380.h +++ b/drivers/scsi/NCR5380.h @@ -292,8 +292,14 @@ static void NCR5380_reselect(struct Scsi_Host *instance); static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *, struct scsi_cmnd *); static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data); static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data); -static int NCR5380_poll_politely(struct Scsi_Host *, int, int, int, int); static int NCR5380_poll_politely2(struct Scsi_Host *, int, int, int, int, int, int, int); +static inline int NCR5380_poll_politely(struct Scsi_Host *instance, + int reg, int bit, int val, int wait) +{ + return NCR5380_poll_politely2(instance, reg, bit, val, + reg, bit, val, wait); +} + #endif /* __KERNEL__ */ #endif /* NCR5380_H */ From ee6a8773cfadc1383a6b6ea4e52c0d93ebdf292a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 28 Aug 2016 12:24:48 +0100 Subject: [PATCH 083/173] scsi: qla2xxx: fix spelling mistake "retyring" -> "retrying" Trivial fix to spelling mistakes in ql_dbg messages. Signed-off-by: Colin Ian King Acked-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_os.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 1dd865028edd..ace65db1d2a2 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -4694,7 +4694,7 @@ retry_unlock: qla83xx_wait_logic(); retry++; ql_dbg(ql_dbg_p3p, base_vha, 0xb064, - "Failed to release IDC lock, retyring=%d\n", retry); + "Failed to release IDC lock, retrying=%d\n", retry); goto retry_unlock; } } else if (retry < 10) { @@ -4702,7 +4702,7 @@ retry_unlock: qla83xx_wait_logic(); retry++; ql_dbg(ql_dbg_p3p, base_vha, 0xb065, - "Failed to read drv-lockid, retyring=%d\n", retry); + "Failed to read drv-lockid, retrying=%d\n", retry); goto retry_unlock; } @@ -4718,7 +4718,7 @@ retry_unlock2: qla83xx_wait_logic(); retry++; ql_dbg(ql_dbg_p3p, base_vha, 0xb066, - "Failed to release IDC lock, retyring=%d\n", retry); + "Failed to release IDC lock, retrying=%d\n", retry); goto retry_unlock2; } } From d67e8b385fac90c100fb65b9534616769cd3f9b8 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Tue, 30 Aug 2016 19:26:00 +0800 Subject: [PATCH 084/173] scsi: move function declarations to scsi_priv.h We get 2 warnings about global functions without a declaration in the scsi driver when building with W=1: drivers/scsi/scsi_lib.c:467:6: warning: no previous prototype for 'scsi_requeue_run_queue' [-Wmissing-prototypes] drivers/scsi/scsi_lib.c:2609:6: warning: no previous prototype for 'scsi_evt_thread' [-Wmissing-prototypes] In fact, both functions are declared in drivers/scsi/scsi_scan.c but need to move them into scsi_priv.h. Signed-off-by: Baoyou Xie Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_priv.h | 2 ++ drivers/scsi/scsi_scan.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 57a4b9973320..ff3cde397a8d 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -85,12 +85,14 @@ extern void scsi_device_unbusy(struct scsi_device *sdev); extern void scsi_queue_insert(struct scsi_cmnd *cmd, int reason); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); extern void scsi_run_host_queues(struct Scsi_Host *shost); +extern void scsi_requeue_run_queue(struct work_struct *work); extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev); extern struct request_queue *scsi_mq_alloc_queue(struct scsi_device *sdev); extern int scsi_mq_setup_tags(struct Scsi_Host *shost); extern void scsi_mq_destroy_tags(struct Scsi_Host *shost); extern int scsi_init_queue(void); extern void scsi_exit_queue(void); +extern void scsi_evt_thread(struct work_struct *work); struct request_queue; struct request; extern struct kmem_cache *scsi_sdb_cache; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index e0a78f53d809..212e98d940bc 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -217,8 +217,6 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, struct scsi_device *sdev; int display_failure_msg = 1, ret; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); - extern void scsi_evt_thread(struct work_struct *work); - extern void scsi_requeue_run_queue(struct work_struct *work); sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size, GFP_ATOMIC); From 02ccda2a2ffba8750dcebe13a09ce5f671ffcda6 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Mon, 29 Aug 2016 18:46:51 +0800 Subject: [PATCH 085/173] scsi: qla4xxx: Mark symbols static where possible We get 1 warning when build kernel with W=1: drivers/scsi/qla4xxx/ql4_nx.c:1846:10: warning: no previous prototype for 'ql4_84xx_ipmdio_rd_reg' [-Wmissing-prototypes] In fact, this function is only used in the file in which it is declared and don't need a declaration, but can be made static. This patch marks this function with 'static'. Signed-off-by: Baoyou Xie Acked-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla4xxx/ql4_nx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index ae87d6c19f17..06ddd13cb7cc 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -1843,7 +1843,7 @@ static uint32_t ql4_84xx_poll_wait_for_ready(struct scsi_qla_host *ha, return rval; } -uint32_t ql4_84xx_ipmdio_rd_reg(struct scsi_qla_host *ha, uint32_t addr1, +static uint32_t ql4_84xx_ipmdio_rd_reg(struct scsi_qla_host *ha, uint32_t addr1, uint32_t addr3, uint32_t mask, uint32_t addr, uint32_t *data_ptr) { From 5f6ac2cee25cb0bf6ec07249a6180cabd46a1913 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 30 Aug 2016 17:36:16 +0100 Subject: [PATCH 086/173] scsi: bfa: Do not dereference port before it is null checked Port is deferenced before it is null sanity checked, hence we potentially have a null pointer dereference bug. Instead, initialise trl_enabled from port->fcs->bfa after we are sure port is not null. Signed-off-by: Colin Ian King Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/bfa/bfa_fcs_lport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c index 7733ad5305d4..4ddda72f60e6 100644 --- a/drivers/scsi/bfa/bfa_fcs_lport.c +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -5827,13 +5827,13 @@ bfa_fcs_lport_get_rport_max_speed(bfa_fcs_lport_t *port) bfa_port_speed_t max_speed = 0; struct bfa_port_attr_s port_attr; bfa_port_speed_t port_speed, rport_speed; - bfa_boolean_t trl_enabled = bfa_fcport_is_ratelim(port->fcs->bfa); - + bfa_boolean_t trl_enabled; if (port == NULL) return 0; fcs = port->fcs; + trl_enabled = bfa_fcport_is_ratelim(port->fcs->bfa); /* Get Physical port's current speed */ bfa_fcport_get_attr(port->fcs->bfa, &port_attr); From 77d4f08024d86974d3eddc6d0a7143f74974783f Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Wed, 31 Aug 2016 00:33:05 +0530 Subject: [PATCH 087/173] scsi: mptfc: Remove deprecated create_singlethread_workqueue The workqueue "fc_rescan_work_q" queues multiple work items viz &ioc->fc_rescan_work, &ioc->fc_lsc_work, &ioc->fc_setup_reset_work, which require strict execution ordering. Hence, an ordered dedicated workqueue has been used. WQ_MEM_RECLAIM has been set since the workqueue is belongs to a storage driver which is being used on a memory reclaim path and hence, requires forward progress under memory pressure. Signed-off-by: Bhaktipriya Shridhar Acked-by: Tejun Heo Signed-off-by: Martin K. Petersen --- drivers/message/fusion/mptfc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index d8bf84aef602..129e132268ff 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -1324,7 +1324,8 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) snprintf(ioc->fc_rescan_work_q_name, sizeof(ioc->fc_rescan_work_q_name), "mptfc_wq_%d", sh->host_no); ioc->fc_rescan_work_q = - create_singlethread_workqueue(ioc->fc_rescan_work_q_name); + alloc_ordered_workqueue(ioc->fc_rescan_work_q_name, + WQ_MEM_RECLAIM); if (!ioc->fc_rescan_work_q) goto out_mptfc_probe; From b2990536f44752e54dc300a2f98253519adfe649 Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Wed, 31 Aug 2016 00:33:25 +0530 Subject: [PATCH 088/173] scsi: mpt: Remove deprecated create_singlethread_workqueue The workqueues "ioc->reset_work_q" and "ioc->fw_event_q" queue a single work item &ioc->fault_reset_work and &fw_event->work, respectively and hence don't require ordering. Hence, they have been converted to use alloc_workqueue(). The WQ_MEM_RECLAIM flag has been set to ensure forward progress under memory pressure since the workqueue belongs to a storage driver which is being used on a memory reclaim path. Since there are fixed number of work items, explicit concurrency limit is unnecessary here. Signed-off-by: Bhaktipriya Shridhar Acked-by: Tejun Heo Signed-off-by: Martin K. Petersen --- drivers/message/fusion/mptbase.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 5537f8df8512..89c7ed16b4df 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -1865,8 +1865,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) snprintf(ioc->reset_work_q_name, MPT_KOBJ_NAME_LEN, "mpt_poll_%d", ioc->id); - ioc->reset_work_q = - create_singlethread_workqueue(ioc->reset_work_q_name); + ioc->reset_work_q = alloc_workqueue(ioc->reset_work_q_name, + WQ_MEM_RECLAIM, 0); if (!ioc->reset_work_q) { printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n", ioc->name); @@ -1992,7 +1992,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ioc->fw_event_list); spin_lock_init(&ioc->fw_event_lock); snprintf(ioc->fw_event_q_name, MPT_KOBJ_NAME_LEN, "mpt/%d", ioc->id); - ioc->fw_event_q = create_singlethread_workqueue(ioc->fw_event_q_name); + ioc->fw_event_q = alloc_workqueue(ioc->fw_event_q_name, + WQ_MEM_RECLAIM, 0); if (!ioc->fw_event_q) { printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n", ioc->name); From a60eec0251fe1bfc0cd549c073591e6657761158 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:11 -0500 Subject: [PATCH 089/173] scsi: smartpqi: change aio sg processing Take advantage of controller improvements. Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 66 +++++++++++++++------------ 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 906f1aaf7cd2..418f63670d71 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -4263,48 +4263,58 @@ static int pqi_build_aio_sg_list(struct pqi_ctrl_info *ctrl_info, int i; u16 iu_length; int sg_count; - unsigned int num_sg_in_iu = 0; + bool chained; + unsigned int num_sg_in_iu; + unsigned int max_sg_per_iu; struct scatterlist *sg; struct pqi_sg_descriptor *sg_descriptor; sg_count = scsi_dma_map(scmd); if (sg_count < 0) return sg_count; + + iu_length = offsetof(struct pqi_aio_path_request, sg_descriptors) - + PQI_REQUEST_HEADER_LENGTH; + num_sg_in_iu = 0; + if (sg_count == 0) goto out; - if (sg_count <= ctrl_info->max_sg_per_iu) { - sg_descriptor = &request->sg_descriptors[0]; - scsi_for_each_sg(scmd, sg, sg_count, i) { - pqi_set_sg_descriptor(sg_descriptor, sg); - sg_descriptor++; - } - put_unaligned_le32(CISS_SG_LAST, - &request->sg_descriptors[sg_count - 1].flags); - num_sg_in_iu = sg_count; - } else { - sg_descriptor = &request->sg_descriptors[0]; - put_unaligned_le64((u64)io_request->sg_chain_buffer_dma_handle, - &sg_descriptor->address); - put_unaligned_le32(sg_count * sizeof(*sg_descriptor), - &sg_descriptor->length); - put_unaligned_le32(CISS_SG_CHAIN, &sg_descriptor->flags); + sg = scsi_sglist(scmd); + sg_descriptor = request->sg_descriptors; + max_sg_per_iu = ctrl_info->max_sg_per_iu - 1; + chained = false; + i = 0; - sg_descriptor = io_request->sg_chain_buffer; - scsi_for_each_sg(scmd, sg, sg_count, i) { - pqi_set_sg_descriptor(sg_descriptor, sg); - sg_descriptor++; + while (1) { + pqi_set_sg_descriptor(sg_descriptor, sg); + if (!chained) + num_sg_in_iu++; + i++; + if (i == sg_count) + break; + sg_descriptor++; + if (i == max_sg_per_iu) { + put_unaligned_le64( + (u64)io_request->sg_chain_buffer_dma_handle, + &sg_descriptor->address); + put_unaligned_le32((sg_count - num_sg_in_iu) + * sizeof(*sg_descriptor), + &sg_descriptor->length); + put_unaligned_le32(CISS_SG_CHAIN, + &sg_descriptor->flags); + chained = true; + num_sg_in_iu++; + sg_descriptor = io_request->sg_chain_buffer; } - put_unaligned_le32(CISS_SG_LAST, - &io_request->sg_chain_buffer[sg_count - 1].flags); - num_sg_in_iu = 1; - request->partial = 1; + sg = sg_next(sg); } -out: - iu_length = offsetof(struct pqi_aio_path_request, sg_descriptors) - - PQI_REQUEST_HEADER_LENGTH; + put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags); + request->partial = chained; iu_length += num_sg_in_iu * sizeof(*sg_descriptor); + +out: put_unaligned_le16(iu_length, &request->header.iu_length); request->num_sg_descriptors = num_sg_in_iu; From b17f048658c9b1bc8ac1d9a54b223f740c70f8fd Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:17 -0500 Subject: [PATCH 090/173] scsi: smartpqi: change tmf macro names small change to make code look cleaner Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi.h | 6 ++---- drivers/scsi/smartpqi/smartpqi_init.c | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index ea4900588f48..dbcdb03feabf 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -545,10 +545,8 @@ typedef u32 pqi_index_t; #define SOP_TASK_ATTRIBUTE_ORDERED 2 #define SOP_TASK_ATTRIBUTE_ACA 4 -#define SOP_TASK_MANAGEMENT_FUNCTION_COMPLETE 0x0 -#define SOP_TASK_MANAGEMENT_FUNCTION_REJECTED 0x4 -#define SOP_TASK_MANAGEMENT_FUNCTION_FAILED 0x5 -#define SOP_TASK_MANAGEMENT_FUNCTION_SUCCEEDED 0x8 +#define SOP_TMF_COMPLETE 0x0 +#define SOP_TMF_FUNCTION_SUCCEEDED 0x8 /* additional CDB bytes usage field codes */ #define SOP_ADDITIONAL_CDB_BYTES_0 0 /* 16-byte CDB */ diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 418f63670d71..6eab38e0d5e9 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -2469,8 +2469,8 @@ static int pqi_interpret_task_management_response( int rc; switch (response->response_code) { - case SOP_TASK_MANAGEMENT_FUNCTION_COMPLETE: - case SOP_TASK_MANAGEMENT_FUNCTION_SUCCEEDED: + case SOP_TMF_COMPLETE: + case SOP_TMF_FUNCTION_SUCCEEDED: rc = 0; break; default: From 77668f412dbcd6a9dd04c92f0b170c5b5182a5fb Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:23 -0500 Subject: [PATCH 091/173] scsi: smartpqi: simplify spanning Removed the workaround for the transition to spanning. Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 38 ++++++++++++++------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 6eab38e0d5e9..9922e31f0b14 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -3663,6 +3663,18 @@ static int pqi_validate_device_capability(struct pqi_ctrl_info *ctrl_info) return -EINVAL; } + if (!ctrl_info->inbound_spanning_supported) { + dev_err(&ctrl_info->pci_dev->dev, + "the controller does not support inbound spanning\n"); + return -EINVAL; + } + + if (ctrl_info->outbound_spanning_supported) { + dev_err(&ctrl_info->pci_dev->dev, + "the controller supports outbound spanning but this driver does not\n"); + return -EINVAL; + } + return 0; } @@ -4138,24 +4150,14 @@ static void pqi_calculate_queue_resources(struct pqi_ctrl_info *ctrl_info) ctrl_info->num_queue_groups = num_queue_groups; - if (ctrl_info->max_inbound_iu_length_per_firmware == 256 && - ctrl_info->outbound_spanning_supported) { - /* - * TEMPHACK - * This is older f/w that doesn't actually support spanning. - */ - ctrl_info->max_inbound_iu_length = - PQI_OPERATIONAL_IQ_ELEMENT_LENGTH; - } else { - /* - * Make sure that the max. inbound IU length is an even multiple - * of our inbound element length. - */ - ctrl_info->max_inbound_iu_length = - (ctrl_info->max_inbound_iu_length_per_firmware / - PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) * - PQI_OPERATIONAL_IQ_ELEMENT_LENGTH; - } + /* + * Make sure that the max. inbound IU length is an even multiple + * of our inbound element length. + */ + ctrl_info->max_inbound_iu_length = + (ctrl_info->max_inbound_iu_length_per_firmware / + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) * + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH; num_elements_per_iq = (ctrl_info->max_inbound_iu_length / From e58081a714275d1490e470bdaf1b5dc23043cf2a Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:29 -0500 Subject: [PATCH 092/173] scsi: smartpqi: enhance drive offline informational message Made a couple of error messages more verbose. Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 9922e31f0b14..198a7c2ab960 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -2298,11 +2298,16 @@ static inline void pqi_aio_path_disabled(struct pqi_io_request *io_request) static inline void pqi_take_device_offline(struct scsi_device *sdev) { struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; if (scsi_device_online(sdev)) { scsi_device_set_state(sdev, SDEV_OFFLINE); ctrl_info = shost_to_hba(sdev->host); schedule_delayed_work(&ctrl_info->rescan_work, 0); + device = sdev->hostdata; + dev_err(&ctrl_info->pci_dev->dev, "offlined scsi %d:%d:%d:%d\n", + ctrl_info->scsi_host->host_no, device->bus, + device->target, device->lun); } } From 14bb215d09de98a8e95fa2bb1b8f35b79672c5df Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:35 -0500 Subject: [PATCH 093/173] scsi: smartpqi: enhance reset logic Eliminated timeout from LUN reset logic. Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi.h | 2 - drivers/scsi/smartpqi/smartpqi_init.c | 109 +++++++++----------------- 2 files changed, 39 insertions(+), 72 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index dbcdb03feabf..053be6be2077 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -370,7 +370,6 @@ struct pqi_task_management_request { }; #define SOP_TASK_MANAGEMENT_LUN_RESET 0x8 -#define PQI_ABORT_TIMEOUT_MSECS (20 * 1000) struct pqi_task_management_response { struct pqi_iu_header header; @@ -762,7 +761,6 @@ struct pqi_scsi_dev { struct pqi_sas_port *sas_port; struct scsi_device *sdev; - bool reset_in_progress; struct list_head scsi_device_list_entry; struct list_head new_device_list_entry; diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 198a7c2ab960..dbc8b40cd8ca 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -4537,13 +4537,6 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, bool raid_bypassed; device = scmd->device->hostdata; - - if (device->reset_in_progress) { - set_host_byte(scmd, DID_RESET); - pqi_scsi_done(scmd); - return 0; - } - ctrl_info = shost_to_hba(shost); if (pqi_ctrl_offline(ctrl_info)) { @@ -4585,53 +4578,7 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, return rc; } -static inline void pqi_complete_queued_requests_queue_group( - struct pqi_queue_group *queue_group, - struct pqi_scsi_dev *device_in_reset) -{ - unsigned int path; - unsigned long flags; - struct pqi_io_request *io_request; - struct pqi_io_request *next; - struct scsi_cmnd *scmd; - struct pqi_scsi_dev *device; - - for (path = 0; path < 2; path++) { - spin_lock_irqsave(&queue_group->submit_lock[path], flags); - - list_for_each_entry_safe(io_request, next, - &queue_group->request_list[path], - request_list_entry) { - scmd = io_request->scmd; - if (!scmd) - continue; - device = scmd->device->hostdata; - if (device == device_in_reset) { - set_host_byte(scmd, DID_RESET); - pqi_scsi_done(scmd); - list_del(&io_request-> - request_list_entry); - } - } - - spin_unlock_irqrestore(&queue_group->submit_lock[path], flags); - } -} - -static void pqi_complete_queued_requests(struct pqi_ctrl_info *ctrl_info, - struct pqi_scsi_dev *device_in_reset) -{ - unsigned int i; - struct pqi_queue_group *queue_group; - - for (i = 0; i < ctrl_info->num_queue_groups; i++) { - queue_group = &ctrl_info->queue_groups[i]; - pqi_complete_queued_requests_queue_group(queue_group, - device_in_reset); - } -} - -static void pqi_reset_lun_complete(struct pqi_io_request *io_request, +static void pqi_lun_reset_complete(struct pqi_io_request *io_request, void *context) { struct completion *waiting = context; @@ -4639,7 +4586,39 @@ static void pqi_reset_lun_complete(struct pqi_io_request *io_request, complete(waiting); } -static int pqi_reset_lun(struct pqi_ctrl_info *ctrl_info, +#define PQI_LUN_RESET_TIMEOUT_SECS 10 + +static int pqi_wait_for_lun_reset_completion(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct completion *wait) +{ + int rc; + unsigned int wait_secs = 0; + + while (1) { + if (wait_for_completion_io_timeout(wait, + PQI_LUN_RESET_TIMEOUT_SECS * HZ)) { + rc = 0; + break; + } + + pqi_check_ctrl_health(ctrl_info); + if (pqi_ctrl_offline(ctrl_info)) { + rc = -ETIMEDOUT; + break; + } + + wait_secs += PQI_LUN_RESET_TIMEOUT_SECS; + + dev_err(&ctrl_info->pci_dev->dev, + "resetting scsi %d:%d:%d:%d - waiting %u seconds\n", + ctrl_info->scsi_host->host_no, device->bus, + device->target, device->lun, wait_secs); + } + + return rc; +} + +static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device) { int rc; @@ -4650,7 +4629,7 @@ static int pqi_reset_lun(struct pqi_ctrl_info *ctrl_info, down(&ctrl_info->lun_reset_sem); io_request = pqi_alloc_io_request(ctrl_info); - io_request->io_complete_callback = pqi_reset_lun_complete; + io_request->io_complete_callback = pqi_lun_reset_complete; io_request->context = &wait; request = io_request->iu; @@ -4668,12 +4647,9 @@ static int pqi_reset_lun(struct pqi_ctrl_info *ctrl_info, &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH, io_request); - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PQI_ABORT_TIMEOUT_MSECS))) { - rc = -ETIMEDOUT; - } else { + rc = pqi_wait_for_lun_reset_completion(ctrl_info, device, &wait); + if (rc == 0) rc = io_request->status; - } pqi_free_io_request(io_request); up(&ctrl_info->lun_reset_sem); @@ -4692,15 +4668,9 @@ static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, if (pqi_ctrl_offline(ctrl_info)) return FAILED; - device->reset_in_progress = true; - pqi_complete_queued_requests(ctrl_info, device); - rc = pqi_reset_lun(ctrl_info, device); - device->reset_in_progress = false; + rc = pqi_lun_reset(ctrl_info, device); - if (rc) - return FAILED; - - return SUCCESS; + return rc == 0 ? SUCCESS : FAILED; } static int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd) @@ -4710,7 +4680,6 @@ static int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd) struct pqi_scsi_dev *device; ctrl_info = shost_to_hba(scmd->device->host); - device = scmd->device->hostdata; dev_err(&ctrl_info->pci_dev->dev, From ff6abb7383d2eec6c8c988ff661352e66f245686 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:41 -0500 Subject: [PATCH 094/173] scsi: smartpqi: add kdump support Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi.h | 13 +++++++--- drivers/scsi/smartpqi/smartpqi_init.c | 35 +++++++++++++++++++++++++++ drivers/scsi/smartpqi/smartpqi_sis.c | 10 ++++++++ drivers/scsi/smartpqi/smartpqi_sis.h | 2 ++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index 053be6be2077..5f965adfab32 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -79,11 +79,13 @@ struct pqi_ctrl_registers { __le32 sis_ctrl_to_host_doorbell; /* 9Ch */ u8 reserved3[0xa0 - (0x9c + sizeof(__le32))]; __le32 sis_ctrl_to_host_doorbell_clear; /* A0h */ - u8 reserved4[0xbc - (0xa0 + sizeof(__le32))]; + u8 reserved4[0xb0 - (0xa0 + sizeof(__le32))]; + __le32 sis_driver_scratch; /* B0h */ + u8 reserved5[0xbc - (0xb0 + sizeof(__le32))]; __le32 sis_firmware_status; /* BCh */ - u8 reserved5[0x1000 - (0xbc + sizeof(__le32))]; + u8 reserved6[0x1000 - (0xbc + sizeof(__le32))]; __le32 sis_mailbox[8]; /* 1000h */ - u8 reserved6[0x4000 - (0x1000 + (sizeof(__le32) * 8))]; + u8 reserved7[0x4000 - (0x1000 + (sizeof(__le32) * 8))]; /* * The PQI spec states that the PQI registers should be at * offset 0 from the PCIe BAR 0. However, we can't map @@ -963,6 +965,11 @@ struct pqi_ctrl_info { struct semaphore lun_reset_sem; }; +enum pqi_ctrl_mode { + UNKNOWN, + PQI_MODE +}; + /* * assume worst case: SATA queue depth of 31 minus 4 internal firmware commands */ diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index dbc8b40cd8ca..43bfeaca65db 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -153,6 +153,18 @@ static inline bool pqi_is_hba_lunid(u8 *scsi3addr) return pqi_scsi3addr_equal(scsi3addr, RAID_CTLR_LUNID); } +static inline enum pqi_ctrl_mode pqi_get_ctrl_mode( + struct pqi_ctrl_info *ctrl_info) +{ + return sis_read_driver_scratch(ctrl_info); +} + +static inline void pqi_save_ctrl_mode(struct pqi_ctrl_info *ctrl_info, + enum pqi_ctrl_mode mode) +{ + sis_write_driver_scratch(ctrl_info, mode); +} + #define PQI_RESCAN_WORK_INTERVAL (10 * HZ) static inline void pqi_schedule_rescan_worker(struct pqi_ctrl_info *ctrl_info) @@ -5266,10 +5278,30 @@ out: return rc; } +static int pqi_kdump_init(struct pqi_ctrl_info *ctrl_info) +{ + if (!sis_is_firmware_running(ctrl_info)) + return -ENXIO; + + if (pqi_get_ctrl_mode(ctrl_info) == PQI_MODE) { + sis_disable_msix(ctrl_info); + if (pqi_reset(ctrl_info) == 0) + sis_reenable_sis_mode(ctrl_info); + } + + return 0; +} + static int pqi_ctrl_init(struct pqi_ctrl_info *ctrl_info) { int rc; + if (reset_devices) { + rc = pqi_kdump_init(ctrl_info); + if (rc) + return rc; + } + /* * When the controller comes out of reset, it is always running * in legacy SIS mode. This is so that it can be compatible @@ -5343,6 +5375,7 @@ static int pqi_ctrl_init(struct pqi_ctrl_info *ctrl_info) /* From here on, we are running in PQI mode. */ ctrl_info->pqi_mode_enabled = true; + pqi_save_ctrl_mode(ctrl_info, PQI_MODE); rc = pqi_alloc_admin_queues(ctrl_info); if (rc) { @@ -5878,6 +5911,8 @@ static void __attribute__((unused)) verify_structures(void) sis_ctrl_to_host_doorbell) != 0x9c); BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, sis_ctrl_to_host_doorbell_clear) != 0xa0); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_driver_scratch) != 0xb0); BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, sis_firmware_status) != 0xbc); BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, diff --git a/drivers/scsi/smartpqi/smartpqi_sis.c b/drivers/scsi/smartpqi/smartpqi_sis.c index 5d416a8aa462..71408f9e8f75 100644 --- a/drivers/scsi/smartpqi/smartpqi_sis.c +++ b/drivers/scsi/smartpqi/smartpqi_sis.c @@ -376,6 +376,16 @@ int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info) return rc; } +void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value) +{ + writel(value, &ctrl_info->registers->sis_driver_scratch); +} + +u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info) +{ + return readl(&ctrl_info->registers->sis_driver_scratch); +} + static void __attribute__((unused)) verify_structures(void) { BUILD_BUG_ON(offsetof(struct sis_base_struct, diff --git a/drivers/scsi/smartpqi/smartpqi_sis.h b/drivers/scsi/smartpqi/smartpqi_sis.h index d2ff8d54754b..bd6e7b08338e 100644 --- a/drivers/scsi/smartpqi/smartpqi_sis.h +++ b/drivers/scsi/smartpqi/smartpqi_sis.h @@ -28,5 +28,7 @@ void sis_enable_msix(struct pqi_ctrl_info *ctrl_info); void sis_disable_msix(struct pqi_ctrl_info *ctrl_info); void sis_soft_reset(struct pqi_ctrl_info *ctrl_info); int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info); +void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value); +u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info); #endif /* _SMARTPQI_SIS_H */ From e57a1f9b2fa4326ec289f1d03c658184ed6addb8 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:47 -0500 Subject: [PATCH 095/173] scsi: smartpqi: correct controller offline issue Fixes: 6c223761e 'smartpqi: initial commit of Microsemi smartpqi driver' Fixed a bug where the driver would not free all of the controller resources if the controller ever went offline. Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 43bfeaca65db..d759703da287 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -5609,19 +5609,14 @@ static void pqi_free_ctrl_resources(struct pqi_ctrl_info *ctrl_info) static void pqi_remove_ctrl(struct pqi_ctrl_info *ctrl_info) { - int rc; + cancel_delayed_work_sync(&ctrl_info->rescan_work); + cancel_delayed_work_sync(&ctrl_info->update_time_work); + pqi_remove_all_scsi_devices(ctrl_info); + pqi_unregister_scsi(ctrl_info); - if (ctrl_info->controller_online) { - cancel_delayed_work_sync(&ctrl_info->rescan_work); - cancel_delayed_work_sync(&ctrl_info->update_time_work); - pqi_remove_all_scsi_devices(ctrl_info); - pqi_unregister_scsi(ctrl_info); - ctrl_info->controller_online = false; - } if (ctrl_info->pqi_mode_enabled) { sis_disable_msix(ctrl_info); - rc = pqi_reset(ctrl_info); - if (rc == 0) + if (pqi_reset(ctrl_info) == 0) sis_reenable_sis_mode(ctrl_info); } pqi_free_ctrl_resources(ctrl_info); From 5e6429df9c8b3ab9e0a8d18af5248692ebe41871 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:53 -0500 Subject: [PATCH 096/173] scsi: smartpqi: correct event acknowledgment timeout issue the driver no longer waits for the firmware to consume the event ack IU. Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi.h | 14 +++++++------- drivers/scsi/smartpqi/smartpqi_init.c | 20 -------------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index 5f965adfab32..07b6444d3e0a 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -634,13 +634,6 @@ struct pqi_encryption_info { #define RAID_MAP_MAX_ENTRIES 1024 -#define PQI_RESERVED_IO_SLOTS_LUN_RESET 1 -#define PQI_RESERVED_IO_SLOTS_EVENT_ACK 1 -#define PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS 3 -#define PQI_RESERVED_IO_SLOTS \ - (PQI_RESERVED_IO_SLOTS_LUN_RESET + PQI_RESERVED_IO_SLOTS_EVENT_ACK + \ - PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS) - #define PQI_PHYSICAL_DEVICE_BUS 0 #define PQI_RAID_VOLUME_BUS 1 #define PQI_HBA_BUS 2 @@ -884,6 +877,13 @@ struct pqi_event { __le32 additional_event_id; }; +#define PQI_RESERVED_IO_SLOTS_LUN_RESET 1 +#define PQI_RESERVED_IO_SLOTS_EVENT_ACK PQI_NUM_SUPPORTED_EVENTS +#define PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS 3 +#define PQI_RESERVED_IO_SLOTS \ + (PQI_RESERVED_IO_SLOTS_LUN_RESET + PQI_RESERVED_IO_SLOTS_EVENT_ACK + \ + PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS) + struct pqi_ctrl_info { unsigned int ctrl_id; struct pci_dev *pci_dev; diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index d759703da287..ea822b19b349 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -2642,26 +2642,6 @@ static void pqi_start_event_ack(struct pqi_ctrl_info *ctrl_info, writel(iq_pi, queue_group->iq_pi[RAID_PATH]); spin_unlock_irqrestore(&queue_group->submit_lock[RAID_PATH], flags); - - /* - * We have to special-case this type of request because the firmware - * does not generate an interrupt when this type of request completes. - * Therefore, we have to poll until we see that the firmware has - * consumed the request before we move on. - */ - - timeout = (PQI_EVENT_ACK_TIMEOUT * HZ) + jiffies; - - while (1) { - if (*queue_group->iq_ci[RAID_PATH] == iq_pi) - break; - if (time_after(jiffies, timeout)) { - dev_err(&ctrl_info->pci_dev->dev, - "completing event acknowledge timed out\n"); - break; - } - usleep_range(1000, 2000); - } } static void pqi_acknowledge_event(struct pqi_ctrl_info *ctrl_info, From df7a1fcfc4761e658b60739e2ff4cd148afcae89 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:54:59 -0500 Subject: [PATCH 097/173] scsi: smartpqi: minor function reformating reformatted pqi_num_elements_free() to match the rest of the driver Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index ea822b19b349..bfd2d75463c5 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -2576,8 +2576,7 @@ static unsigned int pqi_process_io_intr(struct pqi_ctrl_info *ctrl_info, } static inline unsigned int pqi_num_elements_free(unsigned int pi, - unsigned int ci, - unsigned int elements_in_queue) + unsigned int ci, unsigned int elements_in_queue) { unsigned int num_elements_used; From 4fbebf1a779d9f6890ddc1df90c497b161dfb34c Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:55:05 -0500 Subject: [PATCH 098/173] scsi: smartpqi: minor tweaks to update time support minor tweaks to update time support Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index bfd2d75463c5..db248c15adaf 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -588,10 +588,6 @@ static void pqi_update_time_worker(struct work_struct *work) ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info, update_time_work); - if (!ctrl_info) { - printk("%s: NULL controller pointer.\n", __func__); - return; - } rc = pqi_write_current_time_to_host_wellness(ctrl_info); if (rc) dev_warn(&ctrl_info->pci_dev->dev, @@ -602,9 +598,9 @@ static void pqi_update_time_worker(struct work_struct *work) } static inline void pqi_schedule_update_time_worker( - struct pqi_ctrl_info *ctrl_info) + struct pqi_ctrl_info *ctrl_info) { - schedule_delayed_work(&ctrl_info->update_time_work, 120); + schedule_delayed_work(&ctrl_info->update_time_work, 0); } static int pqi_report_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd, From 7d81d2b8714ec72462a99875acbf2f976402f3f1 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:55:11 -0500 Subject: [PATCH 099/173] scsi: smartpqi: scsi queuecommand cleanup minor cleanup of scsi queue command function Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index db248c15adaf..f0672ed416cb 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -4514,7 +4514,7 @@ static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, } static int pqi_scsi_queue_command(struct Scsi_Host *shost, - struct scsi_cmnd *scmd) + struct scsi_cmnd *scmd) { int rc; struct pqi_ctrl_info *ctrl_info; @@ -4532,6 +4532,12 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, return 0; } + /* + * This is necessary because the SML doesn't zero out this field during + * error recovery. + */ + scmd->result = 0; + hwq = blk_mq_unique_tag_to_hwq(blk_mq_unique_tag(scmd->request)); if (hwq >= ctrl_info->num_queue_groups) hwq = 0; From d48f8fad1e435eff26c29e8e109c1a50c441e533 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:55:17 -0500 Subject: [PATCH 100/173] scsi: smartpqi: remove timeout for cache flush operations Some cache flush operations can take longer than the timeout value. Best to not impose a time limit to handle all cases. Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index f0672ed416cb..c6dcb3358ebe 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -407,7 +407,6 @@ static int pqi_identify_physical_device(struct pqi_ctrl_info *ctrl_info, } #define SA_CACHE_FLUSH_BUFFER_LENGTH 4 -#define PQI_FLUSH_CACHE_TIMEOUT (30 * 1000) static int pqi_flush_cache(struct pqi_ctrl_info *ctrl_info) { @@ -434,7 +433,7 @@ static int pqi_flush_cache(struct pqi_ctrl_info *ctrl_info) goto out; rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, - 0, NULL, PQI_FLUSH_CACHE_TIMEOUT); + 0, NULL, NO_TIMEOUT); pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, pci_direction); From e8a31ebae1669f05254430d2fced99d77c63fc10 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:55:24 -0500 Subject: [PATCH 101/173] scsi: smartpqi: update Kconfig The aacraid driver will not managage Microsemi smartpqi controllers, but will still manage older aacraid devices. Updated help section. Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Kevin Barnett Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/Kconfig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/smartpqi/Kconfig b/drivers/scsi/smartpqi/Kconfig index 5d77a804543f..97e159c2cecd 100644 --- a/drivers/scsi/smartpqi/Kconfig +++ b/drivers/scsi/smartpqi/Kconfig @@ -37,7 +37,6 @@ config SCSI_SMARTPQI tristate "Microsemi PQI Driver" - default n depends on PCI && SCSI && !S390 select SCSI_SAS_ATTRS select RAID_ATTRS @@ -47,4 +46,9 @@ config SCSI_SMARTPQI To compile this driver as a module, choose M here: the - module will be called smartpqi + module will be called smartpqi. + + Note: the aacraid driver will not manage a smartpqi + controller. You need to enable smartpqi for smartpqi + controllers. For more information, please see + Documentation/scsi/smartpqi.txt From 425b490b2aa745740ea3618e1cdcc2bc37c0d996 Mon Sep 17 00:00:00 2001 From: Don Brace Date: Wed, 31 Aug 2016 14:55:30 -0500 Subject: [PATCH 102/173] scsi: smartpqi: add smartpqi.txt added Documentation/scsi/smartpqi.txt [mkp: applied by hand] Reviewed-by: Kevin Barnett Reviewed-by: Scott Benesh Reviewed-by: Johannes Thumshirn Signed-off-by: Don Brace Reviewed-by: Tomas Henzl Signed-off-by: Martin K. Petersen --- Documentation/scsi/smartpqi.txt | 80 +++++++++++++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 81 insertions(+) create mode 100644 Documentation/scsi/smartpqi.txt diff --git a/Documentation/scsi/smartpqi.txt b/Documentation/scsi/smartpqi.txt new file mode 100644 index 000000000000..ab377d9e5d1b --- /dev/null +++ b/Documentation/scsi/smartpqi.txt @@ -0,0 +1,80 @@ + +SMARTPQI - Microsemi Smart PQI Driver +----------------------------------------- + +This file describes the smartpqi SCSI driver for Microsemi +(http://www.microsemi.com) PQI controllers. The smartpqi driver +is the next generation SCSI driver for Microsemi Corp. The smartpqi +driver is the first SCSI driver to implement the PQI queuing model. + +The smartpqi driver will replace the aacraid driver for Adaptec Series 9 +controllers. Customers running an older kernel (Pre-4.9) using an Adaptec +Series 9 controller will have to configure the smartpqi driver or their +volumes will not be added to the OS. + +For Microsemi smartpqi controller support, enable the smartpqi driver +when configuring the kernel. + +For more information on the PQI Queuing Interface, please see: +http://www.t10.org/drafts.htm +http://www.t10.org/members/w_pqi2.htm + +Supported devices: +------------------ + + +smartpqi specific entries in /sys +----------------------------- + + smartpqi host attributes: + ------------------------- + /sys/class/scsi_host/host*/rescan + /sys/class/scsi_host/host*/version + + The host rescan attribute is a write only attribute. Writing to this + attribute will trigger the driver to scan for new, changed, or removed + devices and notify the SCSI mid-layer of any changes detected. + + The version attribute is read-only and will return the driver version + and the controller firmware version. + For example: + driver: 0.9.13-370 + firmware: 0.01-522 + + smartpqi sas device attributes + ------------------------------ + HBA devices are added to the SAS transport layer. These attributes are + automatically added by the SAS transport layer. + + /sys/class/sas_device/end_device-X:X/sas_address + /sys/class/sas_device/end_device-X:X/enclosure_identifier + /sys/class/sas_device/end_device-X:X/scsi_target_id + +smartpqi specific ioctls: +------------------------- + + For compatibility with applications written for the cciss protocol. + + CCISS_DEREGDISK + CCISS_REGNEWDISK + CCISS_REGNEWD + + The above three ioctls all do exactly the same thing, which is to cause the driver + to rescan for new devices. This does exactly the same thing as writing to the + smartpqi specific host "rescan" attribute. + + CCISS_GETPCIINFO + + Returns PCI domain, bus, device and function and "board ID" (PCI subsystem ID). + + CCISS_GETDRIVVER + + Returns driver version in three bytes encoded as: + (DRIVER_MAJOR << 28) | (DRIVER_MINOR << 24) | (DRIVER_RELEASE << 16) | DRIVER_REVISION; + + CCISS_PASSTHRU + + Allows "BMIC" and "CISS" commands to be passed through to the Smart Storage Array. + These are used extensively by the SSA Array Configuration Utility, SNMP storage + agents, etc. + diff --git a/MAINTAINERS b/MAINTAINERS index fec431c94002..dd4966d30a27 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7829,6 +7829,7 @@ F: drivers/scsi/smartpqi/Kconfig F: drivers/scsi/smartpqi/Makefile F: include/linux/cciss*.h F: include/uapi/linux/cciss*.h +F: Documentation/scsi/smartpqi.txt MN88472 MEDIA DRIVER M: Antti Palosaari From 699bed758b1313f97a5ac78848090e8357d12ab1 Mon Sep 17 00:00:00 2001 From: Kevin Barnett Date: Wed, 31 Aug 2016 14:55:36 -0500 Subject: [PATCH 103/173] scsi: smartpqi: bump driver version [mkp: fixed typo] Reviewed-by: Scott Teel Reviewed-by: Scott Benesh Reviewed-by: Tomas Henzl Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index c6dcb3358ebe..52cfa268ca95 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -38,11 +38,11 @@ #define BUILD_TIMESTAMP #endif -#define DRIVER_VERSION "0.9.9-100" +#define DRIVER_VERSION "0.9.13-370" #define DRIVER_MAJOR 0 #define DRIVER_MINOR 9 -#define DRIVER_RELEASE 9 -#define DRIVER_REVISION 100 +#define DRIVER_RELEASE 13 +#define DRIVER_REVISION 370 #define DRIVER_NAME "Microsemi PQI Driver (v" DRIVER_VERSION ")" #define DRIVER_NAME_SHORT "smartpqi" From 712db3eb2c35e79986268bcd694ba8075445737d Mon Sep 17 00:00:00 2001 From: "Bryant G. Ly" Date: Wed, 31 Aug 2016 11:28:59 -0500 Subject: [PATCH 104/173] scsi: ibmvscsis: Properly deregister target sessions The driver currently doesn't properly deregisters target sessions completely, so this will address that. Signed-off-by: Bryant G. Ly Signed-off-by: Michael Cyr Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index 7e873a7635ec..4cf37d4d0ee3 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -1934,6 +1934,8 @@ static int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport) /* * Release the SCSI I_T Nexus to the emulated ibmvscsis Target Port */ + target_wait_for_sess_cmds(se_sess); + transport_deregister_session_configfs(se_sess); transport_deregister_session(se_sess); tport->ibmv_nexus = NULL; kfree(nexus); From 812902159d4174df9a82948c0445becb865dabec Mon Sep 17 00:00:00 2001 From: "Bryant G. Ly" Date: Wed, 31 Aug 2016 11:29:00 -0500 Subject: [PATCH 105/173] scsi: ibmvscsis: Code cleanup of print statements [mkp: applied by hand] Signed-off-by: Bryant G. Ly Signed-off-by: Michael Cyr Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 26 ++---------------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index 4cf37d4d0ee3..f3c997f0ecba 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -1606,8 +1606,6 @@ static void ibmvscsis_send_messages(struct scsi_info *vscsi) if (!(vscsi->flags & RESPONSE_Q_DOWN)) { list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, list) { - pr_debug("send_messages cmd %p\n", cmd); - iue = cmd->iue; crq->valid = VALID_CMD_RESP_EL; @@ -2556,10 +2554,6 @@ static void ibmvscsis_parse_cmd(struct scsi_info *vscsi, srp->lun.scsi_lun[0] &= 0x3f; - pr_debug("calling submit_cmd, se_cmd %p, lun 0x%llx, cdb 0x%x, attr:%d\n", - &cmd->se_cmd, scsilun_to_int(&srp->lun), (int)srp->cdb[0], - attr); - rc = target_submit_cmd(&cmd->se_cmd, nexus->se_sess, srp->cdb, cmd->sense_buf, scsilun_to_int(&srp->lun), data_len, attr, dir, 0); @@ -3144,8 +3138,6 @@ static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg, long tx_len; long rc = 0; - pr_debug("rdma: dir %d, bytes 0x%x\n", dir, bytes); - if (bytes == 0) return 0; @@ -3197,9 +3189,6 @@ static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg, /* write to client */ struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf; - if (!READ_CMD(srp->cdb)) - print_hex_dump_bytes(" data:", DUMP_PREFIX_NONE, - sg_virt(sgp), buf_len); /* The h_copy_rdma will cause phyp, running in another * partition, to read memory, so we need to make sure * the data has been written out, hence these syncs. @@ -3324,12 +3313,9 @@ cmd_work: rc = ibmvscsis_trans_event(vscsi, crq); } else if (vscsi->flags & TRANS_EVENT) { /* - * if a tranport event has occurred leave + * if a transport event has occurred leave * everything but transport events on the queue - */ - pr_debug("handle_crq, ignoring\n"); - - /* + * * need to decrement the queue index so we can * look at the elment again */ @@ -3695,8 +3681,6 @@ static void ibmvscsis_release_cmd(struct se_cmd *se_cmd) se_cmd); struct scsi_info *vscsi = cmd->adapter; - pr_debug("release_cmd %p, flags %d\n", se_cmd, cmd->flags); - spin_lock_bh(&vscsi->intr_lock); /* Remove from active_q */ list_move_tail(&cmd->list, &vscsi->waiting_rsp); @@ -3716,9 +3700,6 @@ static int ibmvscsis_write_pending(struct se_cmd *se_cmd) struct iu_entry *iue = cmd->iue; int rc; - pr_debug("write_pending, se_cmd %p, length 0x%x\n", - se_cmd, se_cmd->data_length); - rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 1, 1); if (rc) { @@ -3757,9 +3738,6 @@ static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd) uint len = 0; int rc; - pr_debug("queue_data_in, se_cmd %p, length 0x%x\n", - se_cmd, se_cmd->data_length); - rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 1, 1); if (rc) { From f6dbe38edf1ef4929847c90e244204ef4c6b3ce7 Mon Sep 17 00:00:00 2001 From: "Bryant G. Ly" Date: Wed, 31 Aug 2016 11:29:01 -0500 Subject: [PATCH 106/173] scsi: ibmvscsis: Fixed a bug reported by Dan Carpenter SUPPORTED_FORMATS is 1 << 1 so it's never zero. Signed-off-by: Bryant G. Ly Signed-off-by: Michael Cyr Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index f3c997f0ecba..4dd8e5effddb 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -1978,7 +1978,7 @@ static long ibmvscsis_srp_login(struct scsi_info *vscsi, reason = SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED; else if (fmt->buffers & (~SUPPORTED_FORMATS)) reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT; - else if ((fmt->buffers | SUPPORTED_FORMATS) == 0) + else if ((fmt->buffers & SUPPORTED_FORMATS) == 0) reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT; if (vscsi->state == SRP_PROCESSING) From 61b96d5b68a5aa58351a461cae81117ca9744069 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Sat, 27 Aug 2016 23:49:24 +0800 Subject: [PATCH 107/173] scsi: pmcraid: mark symbols static where possible We get 4 warnings about global functions without a declaration in the scsi pmcraid driver when building with W=1: drivers/scsi/pmcraid.c:309:6: warning: no previous prototype for 'pmcraid_init_cmdblk' [-Wmissing-prototypes] drivers/scsi/pmcraid.c:404:6: warning: no previous prototype for 'pmcraid_return_cmd' [-Wmissing-prototypes] drivers/scsi/pmcraid.c:1713:6: warning: no previous prototype for 'pmcraid_ioasc_logger' [-Wmissing-prototypes] drivers/scsi/pmcraid.c:3141:1: warning: no previous prototype for 'pmcraid_init_ioadls' [-Wmissing-prototypes] In fact, these functions are only used in the file in which it is declared and don't need a declaration, but can be made static. so this patch marks it 'static'. Signed-off-by: Baoyou Xie Signed-off-by: Martin K. Petersen --- drivers/scsi/pmcraid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index b2a88200fe54..68a5c347fae9 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -306,7 +306,7 @@ static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth) * Return Value * None */ -void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index) +static void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index) { struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb); dma_addr_t dma_addr = cmd->ioa_cb_bus_addr; @@ -401,7 +401,7 @@ static struct pmcraid_cmd *pmcraid_get_free_cmd( * Return Value: * nothing */ -void pmcraid_return_cmd(struct pmcraid_cmd *cmd) +static void pmcraid_return_cmd(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; @@ -1710,7 +1710,7 @@ static struct pmcraid_ioasc_error *pmcraid_get_error_info(u32 ioasc) * @ioasc: ioasc code * @cmd: pointer to command that resulted in 'ioasc' */ -void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd) +static void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd) { struct pmcraid_ioasc_error *error_info = pmcraid_get_error_info(ioasc); @@ -3137,7 +3137,7 @@ static int pmcraid_eh_host_reset_handler(struct scsi_cmnd *scmd) * returns pointer pmcraid_ioadl_desc, initialized to point to internal * or external IOADLs */ -struct pmcraid_ioadl_desc * +static struct pmcraid_ioadl_desc * pmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount) { struct pmcraid_ioadl_desc *ioadl; From a816b4c67ae68a36a8775484b09ad727e4a28e3d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 28 Aug 2016 22:17:38 +0200 Subject: [PATCH 108/173] scsi: sr: constify sr_pm_ops structure sr_pm_ops, of type struct dev_pm_ops, is never modified, so declare it as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Martin K. Petersen --- drivers/scsi/sr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index ed179348de80..bed2bbd6b923 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -83,7 +83,7 @@ static int sr_init_command(struct scsi_cmnd *SCpnt); static int sr_done(struct scsi_cmnd *); static int sr_runtime_suspend(struct device *dev); -static struct dev_pm_ops sr_pm_ops = { +static const struct dev_pm_ops sr_pm_ops = { .runtime_suspend = sr_runtime_suspend, }; From 151770524b73816a00a785bd613ea80398b33190 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Mon, 29 Aug 2016 18:19:00 +0800 Subject: [PATCH 109/173] scsi: ufs: Add missing header dependencies for tc-dwc-g210 We get 2 warnings when build kernel with W=1: drivers/scsi/ufs/tc-dwc-g210.c:261:5: warning: no previous prototype for 'tc_dwc_g210_config_40_bit' [-Wmissing-prototypes] drivers/scsi/ufs/tc-dwc-g210.c:293:5: warning: no previous prototype for 'tc_dwc_g210_config_20_bit' [-Wmissing-prototypes] In fact, these functions are declared in ufs/tc-dwc-g210.h, so this patch add missing header dependencies Signed-off-by: Baoyou Xie Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/tc-dwc-g210.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/ufs/tc-dwc-g210.c b/drivers/scsi/ufs/tc-dwc-g210.c index 70db6d999ca3..dc03e47f7c58 100644 --- a/drivers/scsi/ufs/tc-dwc-g210.c +++ b/drivers/scsi/ufs/tc-dwc-g210.c @@ -15,6 +15,7 @@ #include "ufshcd-dwc.h" #include "ufshci-dwc.h" +#include "tc-dwc-g210.h" /** * tc_dwc_g210_setup_40bit_rmmi() From 393c8019e9ce1884f232e2d37761c61796e22095 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 3 Sep 2016 09:05:48 +0200 Subject: [PATCH 110/173] scsi: bnx2fc: Simplify code Calling 'list_splice' followed by 'INIT_LIST_HEAD' is equivalent to 'list_splice_init'. This has been spotted with the following coccinelle script: ///// @@ expression y,z; @@ - list_splice(y,z); - INIT_LIST_HEAD(y); + list_splice_init(y,z); Signed-off-by: Christophe JAILLET Acked-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index a5052dd8d7e6..88be12a02764 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -2765,8 +2765,7 @@ static void __exit bnx2fc_mod_exit(void) * held. */ mutex_lock(&bnx2fc_dev_lock); - list_splice(&adapter_list, &to_be_deleted); - INIT_LIST_HEAD(&adapter_list); + list_splice_init(&adapter_list, &to_be_deleted); adapter_count = 0; mutex_unlock(&bnx2fc_dev_lock); From a44199eee7fdd2791b39da88938e6de6f5058632 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Sun, 4 Sep 2016 14:47:10 +0800 Subject: [PATCH 111/173] scsi: aacraid: mark aac_src_select_comm() static We get 1 warning when building kernel with W=1: drivers/scsi/aacraid/src.c:616:5: warning: no previous prototype for 'aac_src_select_comm' [-Wmissing-prototypes] In fact, this function is only used in the file in which it is declared and don't need a declaration, but can be made static. so this patch marks this function with 'static'. Signed-off-by: Baoyou Xie Acked-by: Arnd Bergmann Acked-by: Dave Carroll Signed-off-by: Martin K. Petersen --- drivers/scsi/aacraid/src.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index 28f8b8a1b8a4..0c453880f214 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -613,7 +613,7 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled) * @dev: Adapter * @comm: communications method */ -int aac_src_select_comm(struct aac_dev *dev, int comm) +static int aac_src_select_comm(struct aac_dev *dev, int comm) { switch (comm) { case AAC_COMM_MESSAGE: From 3f7d67da2f3a2770a817d87c25c7cb9f0c09a671 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Sun, 4 Sep 2016 14:52:21 +0800 Subject: [PATCH 112/173] scsi: bnx2fc: Mark symbols static where possible We get a few warnings when building kernel with W=1: drivers/scsi/bnx2fc/bnx2fc_els.c:257:6: warning: no previous prototype for 'bnx2fc_srr_compl' [-Wmissing-prototypes] drivers/scsi/bnx2fc/bnx2fc_els.c:367:6: warning: no previous prototype for 'bnx2fc_rec_compl' [-Wmissing-prototypes] drivers/scsi/bnx2fc/bnx2fc_fcoe.c:628:5: warning: no previous prototype for 'bnx2fc_percpu_io_thread' [-Wmissing-prototypes] drivers/scsi/bnx2fc/bnx2fc_fcoe.c:1413:26: warning: no previous prototype for 'bnx2fc_interface_create' [-Wmissing-prototypes] drivers/scsi/bnx2fc/bnx2fc_hwi.c:997:21: warning: no previous prototype for 'bnx2fc_alloc_work' [-Wmissing-prototypes] drivers/scsi/bnx2fc/bnx2fc_io.c:1082:5: warning: no previous prototype for 'bnx2fc_abts_cleanup' [-Wmissing-prototypes] .... In fact, these functions are only used in the file in which they are declared and don't need a declaration, but can be made static. so this patch marks these functions with 'static'. Signed-off-by: Baoyou Xie Acked-by: Arnd Bergmann Acked-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/bnx2fc/bnx2fc_els.c | 4 ++-- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 9 +++++---- drivers/scsi/bnx2fc/bnx2fc_hwi.c | 2 +- drivers/scsi/bnx2fc/bnx2fc_io.c | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c index 5beea776b9f5..68ca518d34b0 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_els.c +++ b/drivers/scsi/bnx2fc/bnx2fc_els.c @@ -254,7 +254,7 @@ int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp) return rc; } -void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) +static void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) { struct bnx2fc_mp_req *mp_req; struct fc_frame_header *fc_hdr, *fh; @@ -364,7 +364,7 @@ srr_compl_done: kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); } -void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg) +static void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg) { struct bnx2fc_cmd *orig_io_req, *new_io_req; struct bnx2fc_cmd *rec_req; diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 88be12a02764..f9ddb6156f14 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -625,7 +625,7 @@ static void bnx2fc_recv_frame(struct sk_buff *skb) * * @arg: ptr to bnx2fc_percpu_info structure */ -int bnx2fc_percpu_io_thread(void *arg) +static int bnx2fc_percpu_io_thread(void *arg) { struct bnx2fc_percpu_s *p = arg; struct bnx2fc_work *work, *tmp; @@ -1410,9 +1410,10 @@ bind_err: return NULL; } -struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba, - struct net_device *netdev, - enum fip_state fip_mode) +static struct bnx2fc_interface * +bnx2fc_interface_create(struct bnx2fc_hba *hba, + struct net_device *netdev, + enum fip_state fip_mode) { struct fcoe_ctlr_device *ctlr_dev; struct bnx2fc_interface *interface; diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 28c671b609b2..5ff9f89c17c7 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -994,7 +994,7 @@ void bnx2fc_arm_cq(struct bnx2fc_rport *tgt) } -struct bnx2fc_work *bnx2fc_alloc_work(struct bnx2fc_rport *tgt, u16 wqe) +static struct bnx2fc_work *bnx2fc_alloc_work(struct bnx2fc_rport *tgt, u16 wqe) { struct bnx2fc_work *work; work = kzalloc(sizeof(struct bnx2fc_work), GFP_ATOMIC); diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 8f24d60f09d7..f501095f91ac 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1079,7 +1079,7 @@ int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd) return bnx2fc_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET); } -int bnx2fc_abts_cleanup(struct bnx2fc_cmd *io_req) +static int bnx2fc_abts_cleanup(struct bnx2fc_cmd *io_req) { struct bnx2fc_rport *tgt = io_req->tgt; int rc = SUCCESS; From 73615428c7e1289d493578a7c83959e39e9023e5 Mon Sep 17 00:00:00 2001 From: Kiwoong Kim Date: Thu, 8 Sep 2016 16:50:02 +0900 Subject: [PATCH 113/173] scsi: ufs: Fix a wrong string in power mode change I modified a string as described in UFS spec as follow: umpcrs -> upmcrs. [mkp: applied by hand] Signed-off-by: Kiwoong Kim Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index f08d41a2d70b..66f0eb80b93e 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2568,7 +2568,7 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) status = ufshcd_get_upmcrs(hba); if (status != PWR_LOCAL) { dev_err(hba->dev, - "pwr ctrl cmd 0x%0x failed, host umpcrs:0x%x\n", + "pwr ctrl cmd 0x%0x failed, host upmcrs:0x%x\n", cmd->command, status); ret = (status != PWR_OK) ? status : -1; } From 6c46301fc5f8e6f230b9bc92a0eca3bc568918a3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 8 Sep 2016 15:04:41 +0000 Subject: [PATCH 114/173] scsi: ibmvfc: fix typo in parameter description Fix typo in parameter description. Signed-off-by: Wei Yongjun Reviewed-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi/ibmvfc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 6b92169abaeb..56b9f452425b 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -88,7 +88,7 @@ module_param_named(log_level, log_level, uint, 0); MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. " "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]"); module_param_named(cls3_error, cls3_error, uint, 0); -MODULE_PARM_DESC(log_level, "Enable FC Class 3 Error Recovery. " +MODULE_PARM_DESC(cls3_error, "Enable FC Class 3 Error Recovery. " "[Default=" __stringify(IBMVFC_CLS3_ERROR) "]"); static const struct { From bbbfae962b7c221237c0f92547ee0c83f7204747 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Fri, 2 Sep 2016 15:38:48 -0500 Subject: [PATCH 115/173] scsi: cxlflash: Scan host only after the port is ready for I/O When a port link is established, the AFU sends a 'link up' interrupt. After the link is up, corresponding initialization steps are performed on the card. Following that, when the card is ready for I/O, the AFU sends 'login succeeded' interrupt. Today, cxlflash invokes scsi_scan_host() upon receipt of both interrupts. SCSI commands sent to the port prior to the 'login succeeded' interrupt will fail with 'port not available' error. This is not desirable. Moreover, when async_scan is active for the host, subsequent scan calls are terminated with error. Due to this, the scsi_scan_host() call performed after 'login succeeded' interrupt could portentially return error and the devices may not be scanned properly. To avoid this problem, scsi_scan_host() should be called only after the 'login succeeded' interrupt. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 661bb94e2548..b063c41bf673 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -1187,7 +1187,7 @@ static const struct asyc_intr_info ainfo[] = { {SISL_ASTATUS_FC0_LOGI_F, "login failed", 0, CLR_FC_ERROR}, {SISL_ASTATUS_FC0_LOGI_S, "login succeeded", 0, SCAN_HOST}, {SISL_ASTATUS_FC0_LINK_DN, "link down", 0, 0}, - {SISL_ASTATUS_FC0_LINK_UP, "link up", 0, SCAN_HOST}, + {SISL_ASTATUS_FC0_LINK_UP, "link up", 0, 0}, {SISL_ASTATUS_FC1_OTHER, "other error", 1, CLR_FC_ERROR | LINK_RESET}, {SISL_ASTATUS_FC1_LOGO, "target initiated LOGO", 1, 0}, {SISL_ASTATUS_FC1_CRC_T, "CRC threshold exceeded", 1, LINK_RESET}, @@ -1195,7 +1195,7 @@ static const struct asyc_intr_info ainfo[] = { {SISL_ASTATUS_FC1_LOGI_F, "login failed", 1, CLR_FC_ERROR}, {SISL_ASTATUS_FC1_LOGI_S, "login succeeded", 1, SCAN_HOST}, {SISL_ASTATUS_FC1_LINK_DN, "link down", 1, 0}, - {SISL_ASTATUS_FC1_LINK_UP, "link up", 1, SCAN_HOST}, + {SISL_ASTATUS_FC1_LINK_UP, "link up", 1, 0}, {0x0, "", 0, 0} /* terminator */ }; From babf985d1e1b0677cb264acd01319d2b9c8f4327 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Fri, 2 Sep 2016 15:39:16 -0500 Subject: [PATCH 116/173] scsi: cxlflash: Remove the device cleanly in the system shutdown path Commit 704c4b0ddc03 ("cxlflash: Shutdown notify support for CXL Flash cards") was recently introduced to notify the AFU when a system is going down. Due to the position of the cxlflash driver in the device stack, cxlflash devices are _always_ removed during a reboot/shutdown. This can lead to a crash if the cxlflash shutdown hook is invoked _after_ the shutdown hook for the owning virtual PHB. Furthermore, the current implementation of shutdown/remove hooks for cxlflash are not tolerant to being invoked when the device is not enabled. This can also lead to a crash in situations where the remove hook is invoked after the device has been removed via the vPHBs shutdown hook. An example of this scenario would be an EEH reset failure while a reboot/shutdown is in progress. To solve both problems, the shutdown hook for cxlflash is updated to simply remove the device. This path already includes the AFU notification and thus this solution will continue to perform the original intent. At the same time, the remove hook is updated to protect against being called when the device is not enabled. Fixes: 704c4b0ddc03 ("cxlflash: Shutdown notify support for CXL Flash cards") Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index b063c41bf673..4c2559adf723 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -822,17 +822,6 @@ static void notify_shutdown(struct cxlflash_cfg *cfg, bool wait) } } -/** - * cxlflash_shutdown() - shutdown handler - * @pdev: PCI device associated with the host. - */ -static void cxlflash_shutdown(struct pci_dev *pdev) -{ - struct cxlflash_cfg *cfg = pci_get_drvdata(pdev); - - notify_shutdown(cfg, false); -} - /** * cxlflash_remove() - PCI entry point to tear down host * @pdev: PCI device associated with the host. @@ -844,6 +833,11 @@ static void cxlflash_remove(struct pci_dev *pdev) struct cxlflash_cfg *cfg = pci_get_drvdata(pdev); ulong lock_flags; + if (!pci_is_enabled(pdev)) { + pr_debug("%s: Device is disabled\n", __func__); + return; + } + /* If a Task Management Function is active, wait for it to complete * before continuing with remove. */ @@ -2685,7 +2679,7 @@ static struct pci_driver cxlflash_driver = { .id_table = cxlflash_pci_table, .probe = cxlflash_probe, .remove = cxlflash_remove, - .shutdown = cxlflash_shutdown, + .shutdown = cxlflash_remove, .err_handler = &cxlflash_err_handler, }; From 1d3324c382b1a617eb567e3650dcb51f22dfec9a Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Fri, 2 Sep 2016 15:39:30 -0500 Subject: [PATCH 117/173] scsi: cxlflash: Fix to avoid EEH and host reset collisions The EEH reset handler is ignorant to the current state of the driver when processing a frozen event and initiating a device reset. This can be an issue if an EEH event occurs while a user or stack initiated reset is executing. More specifically, if an EEH occurs while the SCSI host reset handler is active, the reset initiated by the EEH thread will likely collide with the host reset thread. This can leave the device in an inconsistent state, or worse, cause a system crash. As a remedy, the EEH handler is updated to evaluate the device state and take appropriate action (proceed, wait, or disconnect host). The host reset handler is also updated to handle situations where an EEH occurred during a host reset. In such situations, the host reset handler will delay reporting back a success to give the EEH reset an opportunity to complete. Signed-off-by: Matthew R. Ochs Acked-by: Uma Krishnan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 4c2559adf723..4ef523505364 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -2042,6 +2042,11 @@ retry: * cxlflash_eh_host_reset_handler() - reset the host adapter * @scp: SCSI command from stack identifying host. * + * Following a reset, the state is evaluated again in case an EEH occurred + * during the reset. In such a scenario, the host reset will either yield + * until the EEH recovery is complete or return success or failure based + * upon the current device state. + * * Return: * SUCCESS as defined in scsi/scsi.h * FAILED as defined in scsi/scsi.h @@ -2074,7 +2079,8 @@ static int cxlflash_eh_host_reset_handler(struct scsi_cmnd *scp) } else cfg->state = STATE_NORMAL; wake_up_all(&cfg->reset_waitq); - break; + ssleep(1); + /* fall through */ case STATE_RESET: wait_event(cfg->reset_waitq, cfg->state != STATE_RESET); if (cfg->state == STATE_NORMAL) @@ -2590,6 +2596,9 @@ out_remove: * @pdev: PCI device struct. * @state: PCI channel state. * + * When an EEH occurs during an active reset, wait until the reset is + * complete and then take action based upon the device state. + * * Return: PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT */ static pci_ers_result_t cxlflash_pci_error_detected(struct pci_dev *pdev, @@ -2603,6 +2612,10 @@ static pci_ers_result_t cxlflash_pci_error_detected(struct pci_dev *pdev, switch (state) { case pci_channel_io_frozen: + wait_event(cfg->reset_waitq, cfg->state != STATE_RESET); + if (cfg->state == STATE_FAILTERM) + return PCI_ERS_RESULT_DISCONNECT; + cfg->state = STATE_RESET; scsi_block_requests(cfg->host); drain_ioctls(cfg); From 05dab43230fdc0d14ca885b473a2740fe017ecb1 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Fri, 2 Sep 2016 15:40:03 -0500 Subject: [PATCH 118/173] scsi: cxlflash: Improve EEH recovery time When an EEH occurs during device initialization, the port timeout logic can cause excessive delays as MMIO reads will fail. Depending on where they are experienced, these delays can lead to a prolonged reset, causing an unnecessary triggering of other timeout logic in the SCSI stack or user applications. To expedite recovery, the port timeout logic is updated to decay the timeout at a much faster rate when in the presence of a likely EEH frozen event. Signed-off-by: Matthew R. Ochs Acked-by: Uma Krishnan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 4ef523505364..42970a40d49b 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -1040,6 +1040,8 @@ static int wait_port_online(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry) do { msleep(delay_us / 1000); status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]); + if (status == U64_MAX) + nretry /= 2; } while ((status & FC_MTIP_STATUS_MASK) != FC_MTIP_STATUS_ONLINE && nretry--); @@ -1071,6 +1073,8 @@ static int wait_port_offline(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry) do { msleep(delay_us / 1000); status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]); + if (status == U64_MAX) + nretry /= 2; } while ((status & FC_MTIP_STATUS_MASK) != FC_MTIP_STATUS_OFFLINE && nretry--); From f80132613d576e7e705344d0c33f3b5e65d9e16a Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Fri, 2 Sep 2016 15:40:20 -0500 Subject: [PATCH 119/173] scsi: cxlflash: Refactor WWPN setup Commit 964497b3bf3f ("cxlflash: Remove dual port online dependency") logically removed the ability for the WWPN setup routine afu_set_wwpn() to return a non-success value. This routine can safely be made a void to simplify the code as there is no longer a need to report a failure. Signed-off-by: Matthew R. Ochs Acked-by: Uma Krishnan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 40 ++++++++---------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 42970a40d49b..b301655f91cd 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -1093,42 +1093,25 @@ static int wait_port_offline(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry) * online. This toggling action can cause this routine to delay up to a few * seconds. When configured to use the internal LUN feature of the AFU, a * failure to come online is overridden. - * - * Return: - * 0 when the WWPN is successfully written and the port comes back online - * -1 when the port fails to go offline or come back up online */ -static int afu_set_wwpn(struct afu *afu, int port, __be64 __iomem *fc_regs, - u64 wwpn) +static void afu_set_wwpn(struct afu *afu, int port, __be64 __iomem *fc_regs, + u64 wwpn) { - int rc = 0; - set_port_offline(fc_regs); - if (!wait_port_offline(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US, FC_PORT_STATUS_RETRY_CNT)) { pr_debug("%s: wait on port %d to go offline timed out\n", __func__, port); - rc = -1; /* but continue on to leave the port back online */ } - if (rc == 0) - writeq_be(wwpn, &fc_regs[FC_PNAME / 8]); - - /* Always return success after programming WWPN */ - rc = 0; + writeq_be(wwpn, &fc_regs[FC_PNAME / 8]); set_port_online(fc_regs); - if (!wait_port_online(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US, FC_PORT_STATUS_RETRY_CNT)) { - pr_err("%s: wait on port %d to go online timed out\n", - __func__, port); + pr_debug("%s: wait on port %d to go online timed out\n", + __func__, port); } - - pr_debug("%s: returning rc=%d\n", __func__, rc); - - return rc; } /** @@ -1629,15 +1612,10 @@ static int init_global(struct cxlflash_cfg *cfg) [FC_CRC_THRESH / 8]); /* Set WWPNs. If already programmed, wwpn[i] is 0 */ - if (wwpn[i] != 0 && - afu_set_wwpn(afu, i, - &afu->afu_map->global.fc_regs[i][0], - wwpn[i])) { - dev_err(dev, "%s: failed to set WWPN on port %d\n", - __func__, i); - rc = -EIO; - goto out; - } + if (wwpn[i] != 0) + afu_set_wwpn(afu, i, + &afu->afu_map->global.fc_regs[i][0], + wwpn[i]); /* Programming WWPN back to back causes additional * offline/online transitions and a PLOGI */ From c4a11827b760ef8dcda26b5731d072b1d8fb7c81 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Fri, 2 Sep 2016 15:40:41 -0500 Subject: [PATCH 120/173] scsi: cxlflash: Fix context reference tracking on detach Commit 888baf069f49 ("scsi: cxlflash: Add kref to context") introduced a kref to the context. In particular, the detach routine was updated to use the kref services for managing the removal and destruction of a context. As part of this change, the tracking mechanism internal to the detach handler was refactored. This introduced a bug that can cause the tracking state to be lost. This can lead to a situation where exclusive access to a context is prematurely [and unknowingly] relinquished for the executing thread. To remedy, only update the tracking state when the kref operation indicates the context was removed. Fixes: 888baf069f49 ("scsi: cxlflash: Add kref to context") Signed-off-by: Matthew R. Ochs Acked-by: Uma Krishnan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/superpipe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index c91fe6fe8d08..9636970d9611 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -912,7 +912,8 @@ static int _cxlflash_disk_detach(struct scsi_device *sdev, * Release the context reference and the sdev reference that * bound this LUN to the context. */ - put_ctx = !kref_put(&ctxi->kref, remove_context); + if (kref_put(&ctxi->kref, remove_context)) + put_ctx = false; scsi_device_put(sdev); out: if (put_ctx) From e6c346f303d41b8799ea224d0faf1b2caa1c98db Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:11 +0800 Subject: [PATCH 121/173] scsi: hisi_sas: save completion queue read pointer Optimise by saving an avoidable read in the cq interrupt. The queue read pointer will only be updated by software, so don't bother re-reading what was already written in the previous interrupt. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 1 + drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 6 ++---- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 5 ++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index ca55ec2974e0..9410335caf41 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -94,6 +94,7 @@ struct hisi_sas_port { struct hisi_sas_cq { struct hisi_hba *hisi_hba; + int rd_point; int id; }; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 1abbc2e162df..3b31b2046559 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1565,14 +1565,11 @@ static irqreturn_t cq_interrupt_v1_hw(int irq, void *p) struct hisi_sas_complete_v1_hdr *complete_queue = (struct hisi_sas_complete_v1_hdr *) hisi_hba->complete_hdr[queue]; - u32 irq_value, rd_point, wr_point; + u32 irq_value, rd_point = cq->rd_point, wr_point; irq_value = hisi_sas_read32(hisi_hba, OQ_INT_SRC); hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue); - - rd_point = hisi_sas_read32(hisi_hba, - COMPL_Q_0_RD_PTR + (0x14 * queue)); wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR + (0x14 * queue)); @@ -1600,6 +1597,7 @@ static irqreturn_t cq_interrupt_v1_hw(int irq, void *p) } /* update rd_point */ + cq->rd_point = rd_point; hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point); return IRQ_HANDLED; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index bf9b693a717f..11006c99de54 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2049,7 +2049,7 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p) struct hisi_sas_slot *slot; struct hisi_sas_itct *itct; struct hisi_sas_complete_v2_hdr *complete_queue; - u32 irq_value, rd_point, wr_point, dev_id; + u32 irq_value, rd_point = cq->rd_point, wr_point, dev_id; int queue = cq->id; complete_queue = hisi_hba->complete_hdr[queue]; @@ -2057,8 +2057,6 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p) hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue); - rd_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_RD_PTR + - (0x14 * queue)); wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR + (0x14 * queue)); @@ -2106,6 +2104,7 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p) } /* update rd_point */ + cq->rd_point = rd_point; hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point); return IRQ_HANDLED; } From 4fde02ad66dc9ceb0b55daca21ef33b43df30ded Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:12 +0800 Subject: [PATCH 122/173] scsi: hisi_sas: save delivery queue write pointer Optimise by saving an avoidable read in the get_free_slot function. The delivery queue write pointer will only be updated by software, so don't bother re-reading what was already written in the previous call to start_delivery function. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 7 +++++++ drivers/scsi/hisi_sas/hisi_sas_main.c | 5 +++++ drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 12 +++++++----- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 9 ++++++--- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 9410335caf41..72c98522bd26 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -98,6 +98,12 @@ struct hisi_sas_cq { int id; }; +struct hisi_sas_dq { + struct hisi_hba *hisi_hba; + int wr_point; + int id; +}; + struct hisi_sas_device { enum sas_device_type dev_type; struct hisi_hba *hisi_hba; @@ -194,6 +200,7 @@ struct hisi_hba { struct Scsi_Host *shost; struct hisi_sas_cq cq[HISI_SAS_MAX_QUEUES]; + struct hisi_sas_dq dq[HISI_SAS_MAX_QUEUES]; struct hisi_sas_phy phy[HISI_SAS_MAX_PHYS]; struct hisi_sas_port port[HISI_SAS_MAX_PHYS]; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 85c73d311e4d..5d5657670d4c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1239,11 +1239,16 @@ static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) for (i = 0; i < hisi_hba->queue_count; i++) { struct hisi_sas_cq *cq = &hisi_hba->cq[i]; + struct hisi_sas_dq *dq = &hisi_hba->dq[i]; /* Completion queue structure */ cq->id = i; cq->hisi_hba = hisi_hba; + /* Delivery queue structure */ + dq->id = i; + dq->hisi_hba = hisi_hba; + /* Delivery queue */ s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS; hisi_hba->cmd_hdr[i] = dma_alloc_coherent(dev, s, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 3b31b2046559..b5374641bdb0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -875,12 +875,13 @@ static int get_wideport_bitmap_v1_hw(struct hisi_hba *hisi_hba, int port_id) static int get_free_slot_v1_hw(struct hisi_hba *hisi_hba, int *q, int *s) { struct device *dev = &hisi_hba->pdev->dev; + struct hisi_sas_dq *dq; u32 r, w; int queue = hisi_hba->queue; while (1) { - w = hisi_sas_read32_relaxed(hisi_hba, - DLVRY_Q_0_WR_PTR + (queue * 0x14)); + dq = &hisi_hba->dq[queue]; + w = dq->wr_point; r = hisi_sas_read32_relaxed(hisi_hba, DLVRY_Q_0_RD_PTR + (queue * 0x14)); if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) { @@ -903,10 +904,11 @@ static void start_delivery_v1_hw(struct hisi_hba *hisi_hba) { int dlvry_queue = hisi_hba->slot_prep->dlvry_queue; int dlvry_queue_slot = hisi_hba->slot_prep->dlvry_queue_slot; + struct hisi_sas_dq *dq = &hisi_hba->dq[dlvry_queue]; - hisi_sas_write32(hisi_hba, - DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), - ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS); + dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS; + hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), + dq->wr_point); } static int prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 11006c99de54..e0c124b6428e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1023,12 +1023,13 @@ static int get_wideport_bitmap_v2_hw(struct hisi_hba *hisi_hba, int port_id) static int get_free_slot_v2_hw(struct hisi_hba *hisi_hba, int *q, int *s) { struct device *dev = &hisi_hba->pdev->dev; + struct hisi_sas_dq *dq; u32 r, w; int queue = hisi_hba->queue; while (1) { - w = hisi_sas_read32_relaxed(hisi_hba, - DLVRY_Q_0_WR_PTR + (queue * 0x14)); + dq = &hisi_hba->dq[queue]; + w = dq->wr_point; r = hisi_sas_read32_relaxed(hisi_hba, DLVRY_Q_0_RD_PTR + (queue * 0x14)); if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) { @@ -1051,9 +1052,11 @@ static void start_delivery_v2_hw(struct hisi_hba *hisi_hba) { int dlvry_queue = hisi_hba->slot_prep->dlvry_queue; int dlvry_queue_slot = hisi_hba->slot_prep->dlvry_queue_slot; + struct hisi_sas_dq *dq = &hisi_hba->dq[dlvry_queue]; + dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS; hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), - ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS); + dq->wr_point); } static int prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba, From f1dc358408330f6ffc4cf3fa6353684f4a976f43 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:13 +0800 Subject: [PATCH 123/173] scsi: hisi_sas: keep CHL_INT2 masked for v2 HW None of the CHL_INT2 interrupts are serviced in the channel irq ISR, so leave the interrupt source masked. The interrupt mask is initially set in init_reg_v2_hw(). Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index e0c124b6428e..25e7c58612c8 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -965,14 +965,8 @@ static void start_phys_v2_hw(unsigned long data) static void phys_init_v2_hw(struct hisi_hba *hisi_hba) { - int i; struct timer_list *timer = &hisi_hba->timer; - for (i = 0; i < hisi_hba->n_phy; i++) { - hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x6a); - hisi_sas_phy_read32(hisi_hba, i, CHL_INT2_MSK); - } - setup_timer(timer, start_phys_v2_hw, (unsigned long)hisi_hba); mod_timer(timer, jiffies + HZ); } From 59ba49f9d6ab0b265f692ae37d9787d6dbb39f00 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:14 +0800 Subject: [PATCH 124/173] scsi: hisi_sas: only zero slot memory when reused Currently the slot memory is zeroed when it is freed and also when it is reused, like in hisi_sas_task_prep(). Optimise by avoiding the redundant zeroing in the free. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 5d5657670d4c..0e487517d08e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -97,7 +97,7 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, slot->task = NULL; slot->port = NULL; hisi_sas_slot_index_free(hisi_hba, slot->idx); - memset(slot, 0, sizeof(*slot)); + /* slot memory is fully zeroed when it is reused */ } EXPORT_SYMBOL_GPL(hisi_sas_slot_task_free); From 433f569601402d6b1f0df17b75327aed082680a4 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:15 +0800 Subject: [PATCH 125/173] scsi: hisi_sas: use safe BITS_PER_BYTE for slot tag size calculation The memory calculation for the tags bitmap should use BITS_PER_BYTE macro instead of coincidental same value of sizeof(unsigned long). Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 0e487517d08e..dcb17a3cbf3f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1309,7 +1309,7 @@ static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) memset(hisi_hba->breakpoint, 0, s); hisi_hba->slot_index_count = max_command_entries; - s = hisi_hba->slot_index_count / sizeof(unsigned long); + s = hisi_hba->slot_index_count / BITS_PER_BYTE; hisi_hba->slot_index_tags = devm_kzalloc(dev, s, GFP_KERNEL); if (!hisi_hba->slot_index_tags) goto err_out; From d0df8f9ad549b8dda643a518fc1907abdaf33976 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:16 +0800 Subject: [PATCH 126/173] scsi: hisi_sas: disable dlvry queues once at reset for v2 hw The Delivery queue enable register should only be written to once at reset for v2 hw. Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 25e7c58612c8..aca2b80a54b7 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -685,9 +685,7 @@ static int reset_hw_v2_hw(struct hisi_hba *hisi_hba) else reset_val = 0x7ffff; - /* Disable all of the DQ */ - for (i = 0; i < HISI_SAS_MAX_QUEUES; i++) - hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0); + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0); /* Disable all of the PHYs */ for (i = 0; i < hisi_hba->n_phy; i++) { From 85bd6cf3be44602a85714ca61442c3b0666dbb62 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:17 +0800 Subject: [PATCH 127/173] scsi: hisi_sas: fix phy8 linkrate calculation in phy_up_v2_hw() In function phy_up_v2_hw(), we needlessly recalculate the phy linkrate for all phys, and the calculation is incorrect for phy8, so remove this code. Signed-off-by: John Garry Signed-off-by: Xiang Chen Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index aca2b80a54b7..ae44443a1288 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1876,9 +1876,6 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) frame_rcvd[i] = __swab32(idaf); } - /* Get the linkrates */ - link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE); - link_rate = (link_rate >> (phy_no * 4)) & 0xf; sas_phy->linkrate = link_rate; hard_phy_linkrate = hisi_sas_phy_read32(hisi_hba, phy_no, HARD_PHY_LINKRATE); From 09fe9ecb18a7add9b05928151998692d96338e41 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:18 +0800 Subject: [PATCH 128/173] scsi: hisi_sas: fix a potential warning for sata disk ejection If hisi_sas_task_prep() fails for a SATA device due to PHY down, we return a failure to libata and also call task_done(), which will cause ata_qc_complete() to be called twice: - first call from hisi_sas_task_prep(), which will clear flag ATA_QCFLAG_ACTIVE - ata_qc_complete() called from libata The warning call trace is as follows: [ 117.070206] [] __ata_qc_complete+0xf4/0x11c [ 117.070208] [] ata_qc_complete+0x180/0x200 [ 117.070210] [] ata_qc_issue+0x110/0x354 [ 117.070212] [] ata_exec_internal_sg+0x240/0x4d0 [ 117.070214] [] ata_exec_internal+0x60/0xa0 [ 117.070217] [] ata_read_log_page+0x188/0x1b4 [ 117.070218] [] ata_eh_analyze_ncq_error+0xa8/0x274 [ 117.070220] [] ata_eh_link_autopsy+0x94/0x8c8 [ 117.070222] [] ata_eh_autopsy+0x34/0xe8 [ 117.070223] [] ata_do_eh+0x28/0xc0 [ 117.070225] [] ata_std_error_handler+0x3c/0x84 [ 117.070227] [] ata_scsi_port_error_handler+0x480/0x674 [ 117.070230] [] async_sas_ata_eh+0x44/0x78 [ 117.070231] [] async_run_entry_fn+0x40/0x104 [ 117.070234] [] process_one_work+0x128/0x2f0 [ 117.070235] [] worker_thread+0x58/0x434 [ 117.070237] [] kthread+0xd4/0xe8 [ 117.070240] [] ret_from_fork+0x10/0x40 The issue is resolved by simply returning a failure status code to the upper layer. Signed-off-by: Xiang Chen Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index dcb17a3cbf3f..d58e2234a665 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -205,26 +205,12 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_hba *hisi_hba, } port = device->port->lldd_port; if (port && !port->port_attached) { - if (sas_protocol_ata(task->task_proto)) { - struct task_status_struct *ts = &task->task_status; + dev_info(dev, "task prep: %s port%d not attach device\n", + (sas_protocol_ata(task->task_proto)) ? + "SATA/STP" : "SAS", + device->port->id); - dev_info(dev, - "task prep: SATA/STP port%d not attach device\n", - device->port->id); - ts->resp = SAS_TASK_COMPLETE; - ts->stat = SAS_PHY_DOWN; - task->task_done(task); - } else { - struct task_status_struct *ts = &task->task_status; - - dev_info(dev, - "task prep: SAS port%d does not attach device\n", - device->port->id); - ts->resp = SAS_TASK_UNDELIVERED; - ts->stat = SAS_PHY_DOWN; - task->task_done(task); - } - return 0; + return SAS_PHY_DOWN; } if (!sas_protocol_ata(task->task_proto)) { From a6f2c7fff90a6fe7a31e764a21b65a809fafa765 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:19 +0800 Subject: [PATCH 129/173] scsi: hisi_sas: set dma mask before allocate DMA memory The device DMA mask was being set after the bulk of the DMA allocations in the driver init, so potentially DMA allocates fail. To resolve, relocate before allocating the DMA memory when initialising the driver. Signed-off-by: Xiang Chen Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index d58e2234a665..2f872f784e10 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1439,6 +1439,12 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev, &hisi_hba->queue_count)) goto err_out; + if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)) && + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) { + dev_err(dev, "No usable DMA addressing method\n"); + goto err_out; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hisi_hba->regs = devm_ioremap_resource(dev, res); if (IS_ERR(hisi_hba->regs)) @@ -1486,13 +1492,6 @@ int hisi_sas_probe(struct platform_device *pdev, hisi_hba = shost_priv(shost); platform_set_drvdata(pdev, sha); - if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)) && - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) { - dev_err(dev, "No usable DMA addressing method\n"); - rc = -EIO; - goto err_out_ha; - } - phy_nr = port_nr = hisi_hba->n_phy; arr_phy = devm_kcalloc(dev, phy_nr, sizeof(void *), GFP_KERNEL); From d82debecf29b4d89e2d17a52dc32c745ba97c999 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:20 +0800 Subject: [PATCH 130/173] scsi: hisi_sas: fix HBA SAS addr endianness for v2 hw The endianness for the SAS address in the TX_ID_DWORD registers is set incorrectly. We see errors like this in the boot log: [ 7.583284] sas: target proto 0x0 at 50000d1108e7923f:0x1f not handled This is due to the host SAS addr not matching the PHY SAS addr in the expander host-attached phy discovery responses. To fix, we byte swap the SAS addr from BE to LE (which is the endianness of the SAS controller). Signed-off-by: John Garry Signed-off-by: Xiang Chen Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index ae44443a1288..ce84211d8482 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -562,13 +562,13 @@ static void config_id_frame_v2_hw(struct hisi_hba *hisi_hba, int phy_no) hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0, __swab32(identify_buffer[0])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1, - identify_buffer[2]); + __swab32(identify_buffer[1])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2, - identify_buffer[1]); + __swab32(identify_buffer[2])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3, - identify_buffer[4]); + __swab32(identify_buffer[3])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4, - identify_buffer[3]); + __swab32(identify_buffer[4])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5, __swab32(identify_buffer[5])); } From 44c4abe08f224817747795fb8d0677888166e4cd Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:21 +0800 Subject: [PATCH 131/173] scsi: hisi_sas: fix HBA SAS addr endianness for v1 hw The endianness for the SAS address in the TX_ID_DWORD registers is set incorrectly. We see errors like this in the boot log for v2 hw (which would have the same issue as v1 hw): [ 7.583284] sas: target proto 0x0 at 50000d1108e7923f:0x1f not handled This is due to the host SAS addr not matching the PHY SAS addr in the expander host-attached phy discovery responses. To fix, we byte swap the SAS addr from BE to LE (which is the endianness of the SAS controller). Signed-off-by: John Garry Signed-off-by: Xiang Chen Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index b5374641bdb0..c030df08596e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -490,13 +490,13 @@ static void config_id_frame_v1_hw(struct hisi_hba *hisi_hba, int phy_no) hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0, __swab32(identify_buffer[0])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1, - identify_buffer[2]); + __swab32(identify_buffer[1])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2, - identify_buffer[1]); + __swab32(identify_buffer[2])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3, - identify_buffer[4]); + __swab32(identify_buffer[3])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4, - identify_buffer[3]); + __swab32(identify_buffer[4])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5, __swab32(identify_buffer[5])); } From 514f16e36b8cb7852fc388faeaccea125884a3cd Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:22 +0800 Subject: [PATCH 132/173] scsi: hisi_sas: remove init_id_frame_v2_hw() Function config_id_frame_v2_hw() is called twice for each PHY during initialisation, which is unneeded. So remove init_id_frame_v2_hw(), which only calls config_id_frame_v2_hw(). We will keep the call to config_id_frame_v2_hw() in start_phy_v2_hw() since it will be used for PHY reset functions. Signed-off-by: John Garry Signed-off-by: Xiang Chen Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index ce84211d8482..5925b93c923c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -573,14 +573,6 @@ static void config_id_frame_v2_hw(struct hisi_hba *hisi_hba, int phy_no) __swab32(identify_buffer[5])); } -static void init_id_frame_v2_hw(struct hisi_hba *hisi_hba) -{ - int i; - - for (i = 0; i < hisi_hba->n_phy; i++) - config_id_frame_v2_hw(hisi_hba, i); -} - static void setup_itct_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { @@ -912,8 +904,6 @@ static int hw_init_v2_hw(struct hisi_hba *hisi_hba) msleep(100); init_reg_v2_hw(hisi_hba); - init_id_frame_v2_hw(hisi_hba); - return 0; } From 8583d3b5b09f08a5b970489cc34a268f500a4a3d Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:23 +0800 Subject: [PATCH 133/173] scsi: hisi_sas: remove init_id_frame_v1_hw() Function config_id_frame_v1_hw() is called twice for each PHY during initialisation, which is unneeded. So remove init_id_frame_v1_hw(), which only calls config_id_frame_v1_hw(). We will keep the call to config_id_frame_v1_hw() in start_phy_v1_hw() since it will be used for PHY reset functions. Signed-off-by: John Garry Signed-off-by: Xiang Chen Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index c030df08596e..c0ac49d8bc8d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -501,14 +501,6 @@ static void config_id_frame_v1_hw(struct hisi_hba *hisi_hba, int phy_no) __swab32(identify_buffer[5])); } -static void init_id_frame_v1_hw(struct hisi_hba *hisi_hba) -{ - int i; - - for (i = 0; i < hisi_hba->n_phy; i++) - config_id_frame_v1_hw(hisi_hba, i); -} - static void setup_itct_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { @@ -774,8 +766,6 @@ static int hw_init_v1_hw(struct hisi_hba *hisi_hba) msleep(100); init_reg_v1_hw(hisi_hba); - init_id_frame_v1_hw(hisi_hba); - return 0; } From 56cc74b9999ff0059007626ef8fab031033a166b Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:24 +0800 Subject: [PATCH 134/173] scsi: hisi_sas: add missing SATA pending device type to v2 hw In setup_itct_v2_hw(), SATA device type SAS_SATA_PENDING is missing, so add it. Note: The HiSi SAS controller does not support SATA PM, so do not handle SAS_SATA_PM_PORT or SAS_SATA_PM. Signed-off-by: John Garry Signed-off-by: Xiang Chen Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 5925b93c923c..735ebff86904 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -594,6 +594,7 @@ static void setup_itct_v2_hw(struct hisi_hba *hisi_hba, qw0 = HISI_SAS_DEV_TYPE_SSP << ITCT_HDR_DEV_TYPE_OFF; break; case SAS_SATA_DEV: + case SAS_SATA_PENDING: if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) qw0 = HISI_SAS_DEV_TYPE_STP << ITCT_HDR_DEV_TYPE_OFF; else From 9c81e2cf7b9f4f0a806abb8d59736246b1b79708 Mon Sep 17 00:00:00 2001 From: John Garry Date: Tue, 6 Sep 2016 23:36:25 +0800 Subject: [PATCH 135/173] scsi: hisi_sas: send three identify before phy up When the v2 hw is attached with many disks through an expander, there may be OOB reset resulting in a PHY going down after the speed is negotiated (very low probability). This issue is resolved by modifying the link control registers to send three identify frames before the PHY is ready (according to 6.10.3.3.2 in SAS 3.0 spec) and close ready when the PHY is down. Signed-off-by: NengLong Zhao Signed-off-by: John Garry Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 735ebff86904..9825a3f49f53 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -117,6 +117,8 @@ #define SL_CONTROL (PORT_BASE + 0x94) #define SL_CONTROL_NOTIFY_EN_OFF 0 #define SL_CONTROL_NOTIFY_EN_MSK (0x1 << SL_CONTROL_NOTIFY_EN_OFF) +#define SL_CONTROL_CTA_OFF 17 +#define SL_CONTROL_CTA_MSK (0x1 << SL_CONTROL_CTA_OFF) #define TX_ID_DWORD0 (PORT_BASE + 0x9c) #define TX_ID_DWORD1 (PORT_BASE + 0xa0) #define TX_ID_DWORD2 (PORT_BASE + 0xa4) @@ -124,6 +126,9 @@ #define TX_ID_DWORD4 (PORT_BASE + 0xaC) #define TX_ID_DWORD5 (PORT_BASE + 0xb0) #define TX_ID_DWORD6 (PORT_BASE + 0xb4) +#define TXID_AUTO (PORT_BASE + 0xb8) +#define TXID_AUTO_CT3_OFF 1 +#define TXID_AUTO_CT3_MSK (0x1 << TXID_AUTO_CT3_OFF) #define RX_IDAF_DWORD0 (PORT_BASE + 0xc4) #define RX_IDAF_DWORD1 (PORT_BASE + 0xc8) #define RX_IDAF_DWORD2 (PORT_BASE + 0xcc) @@ -814,6 +819,8 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x855); hisi_sas_phy_write32(hisi_hba, i, SAS_PHY_CTRL, 0x30b9908); hisi_sas_phy_write32(hisi_hba, i, SL_TOUT_CFG, 0x7d7d7d7d); + hisi_sas_phy_write32(hisi_hba, i, SL_CONTROL, 0x0); + hisi_sas_phy_write32(hisi_hba, i, TXID_AUTO, 0x2); hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 0x10); hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff); hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff); @@ -1901,16 +1908,21 @@ end: static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba) { int res = 0; - u32 phy_cfg, phy_state; + u32 phy_state, sl_ctrl, txid_auto; hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1); - phy_cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG); - phy_state = hisi_sas_read32(hisi_hba, PHY_STATE); - hisi_sas_phy_down(hisi_hba, phy_no, (phy_state & 1 << phy_no) ? 1 : 0); + sl_ctrl = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL); + hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, + sl_ctrl & ~SL_CONTROL_CTA_MSK); + + txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO); + hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO, + txid_auto | TXID_AUTO_CT3_MSK); + hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, CHL_INT0_NOT_RDY_MSK); hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 0); From 4086eae52beab7808b8aa1a9f7d4095eebc247c7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 12 Sep 2016 13:42:12 +0100 Subject: [PATCH 136/173] scsi: megaraid_sas: add in missing white space in error message text A dev_printk message spans two lines and the literal string is missing a white space between words. Add the white space. Signed-off-by: Colin Ian King Acked-by: Sumit Saxena Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 7edc9e21c536..255f79ea9813 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -5782,7 +5782,7 @@ static int megasas_probe_one(struct pci_dev *pdev, &instance->consumer_h); if (!instance->producer || !instance->consumer) { - dev_printk(KERN_DEBUG, &pdev->dev, "Failed to allocate" + dev_printk(KERN_DEBUG, &pdev->dev, "Failed to allocate " "memory for producer, consumer\n"); goto fail_alloc_dma_buf; } From 3e3f5a8a0f03e5af8a243f6b04cb007068481358 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Tue, 13 Sep 2016 10:58:48 +0200 Subject: [PATCH 137/173] scsi: csiostor: Fix completion usage The (re)initializing of the completion object should be done before we trigger the transfer. Doing this after triggering the hardware opens up a race window. Without the timeout we would problaly even deadlock. Use also reinit_completion because we initalize the whole data structure in csio_scscim_init(). There is only one waiter for the completion, therefore there is no need to use complete_all(). Let's make that clear by using complete() instead of complete_all(). The usage pattern of the completion is: waiter context waker context csio_eh_abort_handler() reinit_completion() wait_for_completion_timeout() csio_scsi_err_handler() complete() [mkp: fix typo] Signed-off-by: Daniel Wagner Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/csiostor/csio_scsi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index c2a6f9f29427..89a52b941ea8 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -1721,7 +1721,7 @@ out: /* Wake up waiting threads */ csio_scsi_cmnd(req) = NULL; - complete_all(&req->cmplobj); + complete(&req->cmplobj); } /* @@ -1945,6 +1945,7 @@ csio_eh_abort_handler(struct scsi_cmnd *cmnd) ready = csio_is_lnode_ready(ln); tmo = CSIO_SCSI_ABRT_TMO_MS; + reinit_completion(&ioreq->cmplobj); spin_lock_irq(&hw->lock); rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE)); spin_unlock_irq(&hw->lock); @@ -1964,8 +1965,6 @@ csio_eh_abort_handler(struct scsi_cmnd *cmnd) goto inval_scmnd; } - /* Wait for completion */ - init_completion(&ioreq->cmplobj); wait_for_completion_timeout(&ioreq->cmplobj, msecs_to_jiffies(tmo)); /* FW didnt respond to abort within our timeout */ From ba2f1293e95f071156f5a6660fb2316d4ea15c46 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Tue, 13 Sep 2016 10:58:49 +0200 Subject: [PATCH 138/173] scsi: sym53c8xx_2: Use complete() instead complete_all() There is only one waiter for the completion, therefore there is no need to use complete_all(). Let's make that clear by using complete() instead of complete_all(). The usage pattern of the completion is: waiter context waker context sym_eh_handler() struct completion eh_done init_completion(eh_done) pci_channel_offline() wait_for_completion_timeout(eh_done) sym2_io_resume() complete(eh_done) Signed-off-by: Daniel Wagner Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/sym53c8xx_2/sym_glue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index 5d00e514ff28..d32e3ba8863e 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -1874,7 +1874,7 @@ static void sym2_io_resume(struct pci_dev *pdev) spin_lock_irq(shost->host_lock); if (sym_data->io_reset) - complete_all(sym_data->io_reset); + complete(sym_data->io_reset); spin_unlock_irq(shost->host_lock); } From e8f814202597e7416f6633793ec947d66b611895 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Tue, 13 Sep 2016 10:58:50 +0200 Subject: [PATCH 139/173] scsi: virtio_scsi: Use complete() instead complete_all() There is only one waiter for the completion, therefore there is no need to use complete_all(). Let's make that clear by using complete() instead of complete_all(). The usage pattern of the completion is: waiter context waker context virtscsi_tmf() DECLARE_COMPLETION_ONSTACK() virtscsi_kick_cmd() wait_for_completion() virtscsi_complete_free() complete() Signed-off-by: Daniel Wagner Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/virtio_scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 7dbbb29d24c6..86924ff145ef 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -258,7 +258,7 @@ static void virtscsi_complete_free(struct virtio_scsi *vscsi, void *buf) struct virtio_scsi_cmd *cmd = buf; if (cmd->comp) - complete_all(cmd->comp); + complete(cmd->comp); } static void virtscsi_ctrl_done(struct virtqueue *vq) From 08348b1c9b6f78430fecb120fd0c4b8c044d3639 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Wed, 31 Aug 2016 14:44:56 +1000 Subject: [PATCH 140/173] scsi: ncr5380: Improve interrupt latency during PIO tranfers Large PIO transfers are broken up into chunks to try to avoid disabling local IRQs for long periods. But IRQs are still disabled for too long and this causes SCC FIFO overruns during serial port transfers. This patch reduces the PIO chunk size to reduce interrupt latency to something on the order of milliseconds, at the expense of additional CPU overhead from extra iterations of the NCR5380_main() loop. That CPU overhead is a problem for slow machines (e.g. mac_scsi on 25 MHz 68030) but these machines generally use PDMA not PIO. This patch doesn't make the overhead any worse on my Mac LC III (because it only gets about 510 accesses per ms). This patch decreases disk performance by a fraction of one percent for dmx3191d on my 333 MHz PowerPC 750. Other affected hardware (such as g_NCR5380 on x86) was not tested but 5380 ISA cards generally use PDMA and not PIO. [mkp: fix whitespace] Signed-off-by: Finn Thain Reviewed-by: Laurence Oberman Signed-off-by: Martin K. Petersen --- drivers/scsi/NCR5380.c | 8 ++++---- drivers/scsi/NCR5380.h | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index b58c6a38fc32..db2739079cbb 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -1847,11 +1847,11 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) /* XXX - need to source or sink data here, as appropriate */ } } else { - /* Break up transfer into 3 ms chunks, - * presuming 6 accesses per handshake. + /* Transfer a small chunk so that the + * irq mode lock is not held too long. */ - transfersize = min((unsigned long)cmd->SCp.this_residual, - hostdata->accesses_per_ms / 2); + transfersize = min(cmd->SCp.this_residual, + NCR5380_PIO_CHUNK_SIZE); len = transfersize; NCR5380_transfer_pio(instance, &phase, &len, (unsigned char **)&cmd->SCp.ptr); diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h index 2ed61b5d40e6..965d92339455 100644 --- a/drivers/scsi/NCR5380.h +++ b/drivers/scsi/NCR5380.h @@ -250,6 +250,8 @@ struct NCR5380_cmd { #define NCR5380_CMD_SIZE (sizeof(struct NCR5380_cmd)) +#define NCR5380_PIO_CHUNK_SIZE 256 + static inline struct scsi_cmnd *NCR5380_to_scmd(struct NCR5380_cmd *ncmd_ptr) { return ((struct scsi_cmnd *)ncmd_ptr) - 1; From 86cf7130a58bd2cdd09da72cdadae0dcd5f5db19 Mon Sep 17 00:00:00 2001 From: Don Brace Date: Fri, 9 Sep 2016 16:30:17 -0500 Subject: [PATCH 141/173] scsi: hpsa: Determine device external status earlier Currently we are checking for external status before we are determining if a device is an external device. Reviewed-by: Scott Benesh Reviewed-by: Scott Teel Reviewed-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 030d0023e1d2..332ff1ab7491 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -4270,6 +4270,11 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h) lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position, i, nphysicals, nlogicals, physdev_list, logdev_list); + /* Determine if this is a lun from an external target array */ + tmpdevice->external = + figure_external_status(h, raid_ctlr_position, i, + nphysicals, nlocal_logicals); + /* * Skip over some devices such as a spare. */ @@ -4295,11 +4300,6 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h) continue; } - /* Determine if this is a lun from an external target array */ - tmpdevice->external = - figure_external_status(h, raid_ctlr_position, i, - nphysicals, nlocal_logicals); - figure_bus_target_lun(h, lunaddrbytes, tmpdevice); hpsa_update_device_supports_aborts(h, tmpdevice, lunaddrbytes); this_device = currentsd[ncurrent]; From d49c2077c03d70a59e3063c7d33f00172491ae31 Mon Sep 17 00:00:00 2001 From: Don Brace Date: Fri, 9 Sep 2016 16:30:23 -0500 Subject: [PATCH 142/173] scsi: hpsa: Check for null device pointers A device can be deleted causing NULL pointer issues. Reviewed-by: Scott Benesh Reviewed-by: Scott Teel Reviewed-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 332ff1ab7491..97f50c71b7c9 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -2388,7 +2388,8 @@ static void hpsa_cmd_free_and_done(struct ctlr_info *h, struct CommandList *c, struct scsi_cmnd *cmd) { hpsa_cmd_resolve_and_free(h, c); - cmd->scsi_done(cmd); + if (cmd && cmd->scsi_done) + cmd->scsi_done(cmd); } static void hpsa_retry_cmd(struct ctlr_info *h, struct CommandList *c) @@ -2489,6 +2490,12 @@ static void complete_scsi_command(struct CommandList *cp) ei = cp->err_info; cmd = cp->scsi_cmd; h = cp->h; + + if (!cmd->device) { + cmd->result = DID_NO_CONNECT << 16; + return hpsa_cmd_free_and_done(h, cp, cmd); + } + dev = cmd->device->hostdata; c2 = &h->ioaccel2_cmd_pool[cp->cmdindex]; @@ -2504,8 +2511,15 @@ static void complete_scsi_command(struct CommandList *cp) cmd->result = (DID_OK << 16); /* host byte */ cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */ - if (cp->cmd_type == CMD_IOACCEL2 || cp->cmd_type == CMD_IOACCEL1) - atomic_dec(&cp->phys_disk->ioaccel_cmds_out); + if (cp->cmd_type == CMD_IOACCEL2 || cp->cmd_type == CMD_IOACCEL1) { + if (dev->physical_device && dev->expose_device && + dev->removed) { + cmd->result = DID_NO_CONNECT << 16; + return hpsa_cmd_free_and_done(h, cp, cmd); + } + if (likely(cp->phys_disk != NULL)) + atomic_dec(&cp->phys_disk->ioaccel_cmds_out); + } /* * We check for lockup status here as it may be set for From 8383278d28b9bc598225f163b6644b8b09f2733d Mon Sep 17 00:00:00 2001 From: Scott Teel Date: Fri, 9 Sep 2016 16:30:29 -0500 Subject: [PATCH 143/173] scsi: hpsa: Check for vpd support before sending Before using vendor-specific VPD pages for getting raid_level and device_id, check for page support. If page isn't supported, don't try to use it. Also, pay attention to return status on hpsa_get_device_id. [mkp: fix boolean return warnings reported by kbuild test robot] Reviewed-by: Scott Benesh Reviewed-by: Scott Teel Reviewed-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 50 ++++++++++++++++++++++++++++++----------- drivers/scsi/hpsa_cmd.h | 1 + 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 97f50c71b7c9..5d25e8db281c 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -293,6 +293,8 @@ static int detect_controller_lockup(struct ctlr_info *h); static void hpsa_disable_rld_caching(struct ctlr_info *h); static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h, struct ReportExtendedLUNdata *buf, int bufsize); +static bool hpsa_vpd_page_supported(struct ctlr_info *h, + unsigned char scsi3addr[], u8 page); static int hpsa_luns_changed(struct ctlr_info *h); static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c, struct hpsa_scsi_dev_t *dev, @@ -3088,11 +3090,19 @@ static void hpsa_get_raid_level(struct ctlr_info *h, buf = kzalloc(64, GFP_KERNEL); if (!buf) return; - rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | 0xC1, buf, 64); + + if (!hpsa_vpd_page_supported(h, scsi3addr, + HPSA_VPD_LV_DEVICE_GEOMETRY)) + goto exit; + + rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | + HPSA_VPD_LV_DEVICE_GEOMETRY, buf, 64); + if (rc == 0) *raid_level = buf[8]; if (*raid_level > RAID_UNKNOWN) *raid_level = RAID_UNKNOWN; +exit: kfree(buf); return; } @@ -3450,7 +3460,7 @@ static void hpsa_get_sas_address(struct ctlr_info *h, unsigned char *scsi3addr, } /* Get a device id from inquiry page 0x83 */ -static int hpsa_vpd_page_supported(struct ctlr_info *h, +static bool hpsa_vpd_page_supported(struct ctlr_info *h, unsigned char scsi3addr[], u8 page) { int rc; @@ -3460,7 +3470,7 @@ static int hpsa_vpd_page_supported(struct ctlr_info *h, buf = kzalloc(256, GFP_KERNEL); if (!buf) - return 0; + return false; /* Get the size of the page list first */ rc = hpsa_scsi_do_inquiry(h, scsi3addr, @@ -3487,10 +3497,10 @@ static int hpsa_vpd_page_supported(struct ctlr_info *h, goto exit_supported; exit_unsupported: kfree(buf); - return 0; + return false; exit_supported: kfree(buf); - return 1; + return true; } static void hpsa_get_ioaccel_status(struct ctlr_info *h, @@ -3539,18 +3549,25 @@ static int hpsa_get_device_id(struct ctlr_info *h, unsigned char *scsi3addr, int rc; unsigned char *buf; - if (buflen > 16) - buflen = 16; + /* Does controller have VPD for device id? */ + if (!hpsa_vpd_page_supported(h, scsi3addr, HPSA_VPD_LV_DEVICE_ID)) + return 1; /* not supported */ + buf = kzalloc(64, GFP_KERNEL); if (!buf) return -ENOMEM; - rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | 0x83, buf, 64); - if (rc == 0) - memcpy(device_id, &buf[index], buflen); + + rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | + HPSA_VPD_LV_DEVICE_ID, buf, 64); + if (rc == 0) { + if (buflen > 16) + buflen = 16; + memcpy(device_id, &buf[8], buflen); + } kfree(buf); - return rc != 0; + return rc; /*0 - got id, otherwise, didn't */ } static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical, @@ -3821,8 +3838,15 @@ static int hpsa_update_device_info(struct ctlr_info *h, sizeof(this_device->model)); memset(this_device->device_id, 0, sizeof(this_device->device_id)); - hpsa_get_device_id(h, scsi3addr, this_device->device_id, 8, - sizeof(this_device->device_id)); + if (hpsa_get_device_id(h, scsi3addr, this_device->device_id, 8, + sizeof(this_device->device_id))) + dev_err(&h->pdev->dev, + "hpsa%d: %s: can't get device id for host %d:C0:T%d:L%d\t%s\t%.16s\n", + h->ctlr, __func__, + h->scsi_host->host_no, + this_device->target, this_device->lun, + scsi_device_type(this_device->devtype), + this_device->model); if ((this_device->devtype == TYPE_DISK || this_device->devtype == TYPE_ZBC) && diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index a5be153d92d4..a584cdf07058 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -157,6 +157,7 @@ /* VPD Inquiry types */ #define HPSA_VPD_SUPPORTED_PAGES 0x00 +#define HPSA_VPD_LV_DEVICE_ID 0x83 #define HPSA_VPD_LV_DEVICE_GEOMETRY 0xC1 #define HPSA_VPD_LV_IOACCEL_STATUS 0xC2 #define HPSA_VPD_LV_STATUS 0xC3 From 4b6e5597f4c9ae545a1bd18e830d9f6b4465cadd Mon Sep 17 00:00:00 2001 From: Scott Teel Date: Fri, 9 Sep 2016 16:30:36 -0500 Subject: [PATCH 144/173] scsi: hpsa: Prevent sending bmic commands to externals Reviewed-by: Scott Benesh Reviewed-by: Scott Teel Reviewed-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 5d25e8db281c..82b277be6bf1 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -4072,7 +4072,17 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h, struct bmic_identify_physical_device *id_phys) { int rc; - struct ext_report_lun_entry *rle = &rlep->LUN[rle_index]; + struct ext_report_lun_entry *rle; + + /* + * external targets don't support BMIC + */ + if (dev->external) { + dev->queue_depth = 7; + return; + } + + rle = &rlep->LUN[rle_index]; dev->ioaccel_handle = rle->ioaccel_handle; if ((rle->device_flags & 0x08) && dev->ioaccel_handle) From 45e596cd6ead1c4ada74b03067595d7f4502fa6a Mon Sep 17 00:00:00 2001 From: Don Brace Date: Fri, 9 Sep 2016 16:30:42 -0500 Subject: [PATCH 145/173] scsi: hpsa: Check for null devices in ioaccel submission patch Reviewed-by: Scott Benesh Reviewed-by: Scott Teel Reviewed-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 82b277be6bf1..e5c02d7cb34b 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -2499,6 +2499,10 @@ static void complete_scsi_command(struct CommandList *cp) } dev = cmd->device->hostdata; + if (!dev) { + cmd->result = DID_NO_CONNECT << 16; + return hpsa_cmd_free_and_done(h, cp, cmd); + } c2 = &h->ioaccel2_cmd_pool[cp->cmdindex]; scsi_dma_unmap(cmd); /* undo the DMA mappings */ @@ -4686,6 +4690,9 @@ static int hpsa_scsi_ioaccel_direct_map(struct ctlr_info *h, struct scsi_cmnd *cmd = c->scsi_cmd; struct hpsa_scsi_dev_t *dev = cmd->device->hostdata; + if (!dev) + return -1; + c->phys_disk = dev; return hpsa_scsi_ioaccel_queue_command(h, c, dev->ioaccel_handle, @@ -4762,6 +4769,12 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, u32 len; u32 total_len = 0; + if (!cmd->device) + return -1; + + if (!cmd->device->hostdata) + return -1; + BUG_ON(scsi_sg_count(cmd) > h->maxsgentries); if (fixup_ioaccel_cdb(cdb, &cdb_len)) { @@ -4870,6 +4883,12 @@ static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h, struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len, u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk) { + if (!c->scsi_cmd->device) + return -1; + + if (!c->scsi_cmd->device->hostdata) + return -1; + /* Try to honor the device's queue depth */ if (atomic_inc_return(&phys_disk->ioaccel_cmds_out) > phys_disk->queue_depth) { @@ -4950,6 +4969,9 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h, #endif int offload_to_mirror; + if (!dev) + return -1; + /* check for valid opcode, get LBA and block count */ switch (cmd->cmnd[0]) { case WRITE_6: @@ -5362,6 +5384,9 @@ static int hpsa_ioaccel_submit(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev = cmd->device->hostdata; int rc = IO_ACCEL_INELIGIBLE; + if (!dev) + return SCSI_MLQUEUE_HOST_BUSY; + cmd->host_scribble = (unsigned char *) c; if (dev->offload_enabled) { @@ -5900,6 +5925,9 @@ static void setup_ioaccel2_abort_cmd(struct CommandList *c, struct ctlr_info *h, struct scsi_cmnd *scmd = command_to_abort->scsi_cmd; struct hpsa_scsi_dev_t *dev = scmd->device->hostdata; + if (!dev) + return; + /* * We're overlaying struct hpsa_tmf_struct on top of something which * was allocated as a struct io_accel2_cmd, so we better be sure it @@ -6020,6 +6048,9 @@ static int hpsa_send_abort_ioaccel2(struct ctlr_info *h, struct io_accel2_cmd *c2; dev = abort->scsi_cmd->device->hostdata; + if (!dev) + return -1; + if (!dev->offload_enabled && !dev->hba_ioaccel_enabled) return -1; From c08b3f9a27006c5c541b5c0fa273444a93617fda Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 10 Sep 2016 16:06:36 +0000 Subject: [PATCH 146/173] scsi: fusion: Fix error return code in mptfc_probe() Fix to return error code -ENOMEM from the workqueue alloc error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Martin K. Petersen --- drivers/message/fusion/mptfc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index 129e132268ff..add6a3a6ef0d 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -1326,8 +1326,10 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->fc_rescan_work_q = alloc_ordered_workqueue(ioc->fc_rescan_work_q_name, WQ_MEM_RECLAIM); - if (!ioc->fc_rescan_work_q) + if (!ioc->fc_rescan_work_q) { + error = -ENOMEM; goto out_mptfc_probe; + } /* * Pre-fetch FC port WWN and stuff... From 6ebf105cc047f3877ac530cd19d13b3e788ab02e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 11 Sep 2016 19:35:39 +0200 Subject: [PATCH 147/173] scsi: scsi_debug: Use struct t10_pi_tuple instead of struct sd_dif_tuple And remove the declaration of the latter in sd.h as scsi_debug was the only user. Signed-off-by: Christoph Hellwig Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 15 ++++++++------- drivers/scsi/sd.h | 9 --------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 6a219a0844d3..044fc939043f 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -627,7 +628,7 @@ static LIST_HEAD(sdebug_host_list); static DEFINE_SPINLOCK(sdebug_host_list_lock); static unsigned char *fake_storep; /* ramdisk storage */ -static struct sd_dif_tuple *dif_storep; /* protection info */ +static struct t10_pi_tuple *dif_storep; /* protection info */ static void *map_storep; /* provisioning map */ static unsigned long map_size; @@ -682,7 +683,7 @@ static void *fake_store(unsigned long long lba) return fake_storep + lba * sdebug_sector_size; } -static struct sd_dif_tuple *dif_store(sector_t sector) +static struct t10_pi_tuple *dif_store(sector_t sector) { sector = sector_div(sector, sdebug_store_sectors); @@ -2430,7 +2431,7 @@ static __be16 dif_compute_csum(const void *buf, int len) return csum; } -static int dif_verify(struct sd_dif_tuple *sdt, const void *data, +static int dif_verify(struct t10_pi_tuple *sdt, const void *data, sector_t sector, u32 ei_lba) { __be16 csum = dif_compute_csum(data, sdebug_sector_size); @@ -2504,7 +2505,7 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, unsigned int sectors, u32 ei_lba) { unsigned int i; - struct sd_dif_tuple *sdt; + struct t10_pi_tuple *sdt; sector_t sector; for (i = 0; i < sectors; i++, ei_lba++) { @@ -2696,7 +2697,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, unsigned int sectors, u32 ei_lba) { int ret; - struct sd_dif_tuple *sdt; + struct t10_pi_tuple *sdt; void *daddr; sector_t sector = start_sec; int ppage_offset; @@ -2722,7 +2723,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, } for (ppage_offset = 0; ppage_offset < piter.length; - ppage_offset += sizeof(struct sd_dif_tuple)) { + ppage_offset += sizeof(struct t10_pi_tuple)) { /* If we're at the end of the current * data page advance to the next one */ @@ -5026,7 +5027,7 @@ static int __init scsi_debug_init(void) if (sdebug_dix) { int dif_size; - dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple); + dif_size = sdebug_store_sectors * sizeof(struct t10_pi_tuple); dif_storep = vmalloc(dif_size); pr_err("dif_storep %u bytes @ %p\n", dif_size, dif_storep); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 765a6f1ac1b7..d00966d6b41e 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -239,15 +239,6 @@ static inline unsigned int sd_prot_flag_mask(unsigned int prot_op) return flag_mask[prot_op]; } -/* - * Data Integrity Field tuple. - */ -struct sd_dif_tuple { - __be16 guard_tag; /* Checksum */ - __be16 app_tag; /* Opaque storage */ - __be32 ref_tag; /* Target LBA or indirect LBA */ -}; - #ifdef CONFIG_BLK_DEV_INTEGRITY extern void sd_dif_config_host(struct scsi_disk *); From 27c0e83b273a637d5cb56f02550c31d803bafc10 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 11 Sep 2016 19:35:40 +0200 Subject: [PATCH 148/173] scsi: qla2xxx: Use struct t10_pi_tuple Instead of defining a local version of it. Signed-off-by: Christoph Hellwig Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_def.h | 10 ---------- drivers/scsi/qla2xxx/qla_isr.c | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index ae4a74756128..73b12e41d992 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -278,16 +278,6 @@ struct req_que; struct qla_tgt_sess; -/* - * (sd.h is not exported, hence local inclusion) - * Data Integrity Field tuple. - */ -struct sd_dif_tuple { - __be16 guard_tag; /* Checksum */ - __be16 app_tag; /* Opaque storage */ - __be32 ref_tag; /* Target LBA or indirect LBA */ -}; - /* * SCSI Request Block */ diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 987f1c729e9c..068c4e47fac9 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1828,7 +1828,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) if (scsi_prot_sg_count(cmd)) { uint32_t i, j = 0, k = 0, num_ent; struct scatterlist *sg; - struct sd_dif_tuple *spt; + struct t10_pi_tuple *spt; /* Patch the corresponding protection tags */ scsi_for_each_prot_sg(cmd, sg, From 8475c8118551f806176b5af4d0e8657a5f015b95 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 11 Sep 2016 19:35:41 +0200 Subject: [PATCH 149/173] scsi: sd: Move DIF protection types to t10-pi.h These should go together with the rest of the T10 protection information defintions. [mkp: s/T10_DIF/T10_PI/] Signed-off-by: Christoph Hellwig Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 39 +++++++++++++++++++-------------------- drivers/scsi/sd.c | 11 ++++++----- drivers/scsi/sd.h | 21 --------------------- drivers/scsi/sd_dif.c | 10 +++++----- include/linux/t10-pi.h | 20 ++++++++++++++++++++ 5 files changed, 50 insertions(+), 51 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 044fc939043f..c905709707f0 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1350,7 +1350,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) } else if (0x86 == cmd[2]) { /* extended inquiry */ arr[1] = cmd[2]; /*sanity */ arr[3] = 0x3c; /* number of following entries */ - if (sdebug_dif == SD_DIF_TYPE3_PROTECTION) + if (sdebug_dif == T10_PI_TYPE3_PROTECTION) arr[4] = 0x4; /* SPT: GRD_CHK:1 */ else if (have_dif_prot) arr[4] = 0x5; /* SPT: GRD_CHK:1, REF_CHK:1 */ @@ -2443,13 +2443,13 @@ static int dif_verify(struct t10_pi_tuple *sdt, const void *data, be16_to_cpu(csum)); return 0x01; } - if (sdebug_dif == SD_DIF_TYPE1_PROTECTION && + if (sdebug_dif == T10_PI_TYPE1_PROTECTION && be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) { pr_err("REF check failed on sector %lu\n", (unsigned long)sector); return 0x03; } - if (sdebug_dif == SD_DIF_TYPE2_PROTECTION && + if (sdebug_dif == T10_PI_TYPE2_PROTECTION && be32_to_cpu(sdt->ref_tag) != ei_lba) { pr_err("REF check failed on sector %lu\n", (unsigned long)sector); @@ -2581,13 +2581,13 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) break; } if (unlikely(have_dif_prot && check_prot)) { - if (sdebug_dif == SD_DIF_TYPE2_PROTECTION && + if (sdebug_dif == T10_PI_TYPE2_PROTECTION && (cmd[1] & 0xe0)) { mk_sense_invalid_opcode(scp); return check_condition_result; } - if ((sdebug_dif == SD_DIF_TYPE1_PROTECTION || - sdebug_dif == SD_DIF_TYPE3_PROTECTION) && + if ((sdebug_dif == T10_PI_TYPE1_PROTECTION || + sdebug_dif == T10_PI_TYPE3_PROTECTION) && (cmd[1] & 0xe0) == 0) sdev_printk(KERN_ERR, scp->device, "Unprotected RD " "to DIF device\n"); @@ -2894,13 +2894,13 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) break; } if (unlikely(have_dif_prot && check_prot)) { - if (sdebug_dif == SD_DIF_TYPE2_PROTECTION && + if (sdebug_dif == T10_PI_TYPE2_PROTECTION && (cmd[1] & 0xe0)) { mk_sense_invalid_opcode(scp); return check_condition_result; } - if ((sdebug_dif == SD_DIF_TYPE1_PROTECTION || - sdebug_dif == SD_DIF_TYPE3_PROTECTION) && + if ((sdebug_dif == T10_PI_TYPE1_PROTECTION || + sdebug_dif == T10_PI_TYPE3_PROTECTION) && (cmd[1] & 0xe0) == 0) sdev_printk(KERN_ERR, scp->device, "Unprotected WR " "to DIF device\n"); @@ -3136,13 +3136,13 @@ static int resp_comp_write(struct scsi_cmnd *scp, num = cmd[13]; /* 1 to a maximum of 255 logical blocks */ if (0 == num) return 0; /* degenerate case, not an error */ - if (sdebug_dif == SD_DIF_TYPE2_PROTECTION && + if (sdebug_dif == T10_PI_TYPE2_PROTECTION && (cmd[1] & 0xe0)) { mk_sense_invalid_opcode(scp); return check_condition_result; } - if ((sdebug_dif == SD_DIF_TYPE1_PROTECTION || - sdebug_dif == SD_DIF_TYPE3_PROTECTION) && + if ((sdebug_dif == T10_PI_TYPE1_PROTECTION || + sdebug_dif == T10_PI_TYPE3_PROTECTION) && (cmd[1] & 0xe0) == 0) sdev_printk(KERN_ERR, scp->device, "Unprotected WR " "to DIF device\n"); @@ -4940,12 +4940,11 @@ static int __init scsi_debug_init(void) } switch (sdebug_dif) { - - case SD_DIF_TYPE0_PROTECTION: + case T10_PI_TYPE0_PROTECTION: break; - case SD_DIF_TYPE1_PROTECTION: - case SD_DIF_TYPE2_PROTECTION: - case SD_DIF_TYPE3_PROTECTION: + case T10_PI_TYPE1_PROTECTION: + case T10_PI_TYPE2_PROTECTION: + case T10_PI_TYPE3_PROTECTION: have_dif_prot = true; break; @@ -5481,19 +5480,19 @@ static int sdebug_driver_probe(struct device * dev) switch (sdebug_dif) { - case SD_DIF_TYPE1_PROTECTION: + case T10_PI_TYPE1_PROTECTION: hprot = SHOST_DIF_TYPE1_PROTECTION; if (sdebug_dix) hprot |= SHOST_DIX_TYPE1_PROTECTION; break; - case SD_DIF_TYPE2_PROTECTION: + case T10_PI_TYPE2_PROTECTION: hprot = SHOST_DIF_TYPE2_PROTECTION; if (sdebug_dix) hprot |= SHOST_DIX_TYPE2_PROTECTION; break; - case SD_DIF_TYPE3_PROTECTION: + case T10_PI_TYPE3_PROTECTION: hprot = SHOST_DIF_TYPE3_PROTECTION; if (sdebug_dix) hprot |= SHOST_DIX_TYPE3_PROTECTION; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index d3e852ad5aa3..51e56296f465 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -314,7 +315,7 @@ protection_type_store(struct device *dev, struct device_attribute *attr, if (err) return err; - if (val >= 0 && val <= SD_DIF_TYPE3_PROTECTION) + if (val >= 0 && val <= T10_PI_TYPE3_PROTECTION) sdkp->protection_type = val; return count; @@ -332,7 +333,7 @@ protection_mode_show(struct device *dev, struct device_attribute *attr, dif = scsi_host_dif_capable(sdp->host, sdkp->protection_type); dix = scsi_host_dix_capable(sdp->host, sdkp->protection_type); - if (!dix && scsi_host_dix_capable(sdp->host, SD_DIF_TYPE0_PROTECTION)) { + if (!dix && scsi_host_dix_capable(sdp->host, T10_PI_TYPE0_PROTECTION)) { dif = 0; dix = 1; } @@ -608,7 +609,7 @@ static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, scmd->prot_flags |= SCSI_PROT_GUARD_CHECK; } - if (dif != SD_DIF_TYPE3_PROTECTION) { /* DIX/DIF Type 0, 1, 2 */ + if (dif != T10_PI_TYPE3_PROTECTION) { /* DIX/DIF Type 0, 1, 2 */ scmd->prot_flags |= SCSI_PROT_REF_INCREMENT; if (bio_integrity_flagged(bio, BIP_CTRL_NOCHECK) == false) @@ -1031,7 +1032,7 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) else protect = 0; - if (protect && sdkp->protection_type == SD_DIF_TYPE2_PROTECTION) { + if (protect && sdkp->protection_type == T10_PI_TYPE2_PROTECTION) { SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); if (unlikely(SCpnt->cmnd == NULL)) { @@ -1997,7 +1998,7 @@ static int sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */ - if (type > SD_DIF_TYPE3_PROTECTION) + if (type > T10_PI_TYPE3_PROTECTION) ret = -ENODEV; else if (scsi_host_dif_capable(sdp->host, type)) ret = 1; diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index d00966d6b41e..c8d986368da9 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -156,27 +156,6 @@ static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t b return blocks * sdev->sector_size; } -/* - * A DIF-capable target device can be formatted with different - * protection schemes. Currently 0 through 3 are defined: - * - * Type 0 is regular (unprotected) I/O - * - * Type 1 defines the contents of the guard and reference tags - * - * Type 2 defines the contents of the guard and reference tags and - * uses 32-byte commands to seed the latter - * - * Type 3 defines the contents of the guard tag only - */ - -enum sd_dif_target_protection_types { - SD_DIF_TYPE0_PROTECTION = 0x0, - SD_DIF_TYPE1_PROTECTION = 0x1, - SD_DIF_TYPE2_PROTECTION = 0x2, - SD_DIF_TYPE3_PROTECTION = 0x3, -}; - /* * Look up the DIX operation based on whether the command is read or * write and whether dix and dif are enabled. diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c index 987bf392c336..9035380c0dda 100644 --- a/drivers/scsi/sd_dif.c +++ b/drivers/scsi/sd_dif.c @@ -60,14 +60,14 @@ void sd_dif_config_host(struct scsi_disk *sdkp) /* Enable DMA of protection information */ if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP) { - if (type == SD_DIF_TYPE3_PROTECTION) + if (type == T10_PI_TYPE3_PROTECTION) bi.profile = &t10_pi_type3_ip; else bi.profile = &t10_pi_type1_ip; bi.flags |= BLK_INTEGRITY_IP_CHECKSUM; } else - if (type == SD_DIF_TYPE3_PROTECTION) + if (type == T10_PI_TYPE3_PROTECTION) bi.profile = &t10_pi_type3_crc; else bi.profile = &t10_pi_type1_crc; @@ -82,7 +82,7 @@ void sd_dif_config_host(struct scsi_disk *sdkp) if (!sdkp->ATO) goto out; - if (type == SD_DIF_TYPE3_PROTECTION) + if (type == T10_PI_TYPE3_PROTECTION) bi.tag_size = sizeof(u16) + sizeof(u32); else bi.tag_size = sizeof(u16); @@ -121,7 +121,7 @@ void sd_dif_prepare(struct scsi_cmnd *scmd) sdkp = scsi_disk(scmd->request->rq_disk); - if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION) + if (sdkp->protection_type == T10_PI_TYPE3_PROTECTION) return; phys = scsi_prot_ref_tag(scmd); @@ -172,7 +172,7 @@ void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes) sdkp = scsi_disk(scmd->request->rq_disk); - if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION || good_bytes == 0) + if (sdkp->protection_type == T10_PI_TYPE3_PROTECTION || good_bytes == 0) return; intervals = good_bytes / scsi_prot_interval(scmd); diff --git a/include/linux/t10-pi.h b/include/linux/t10-pi.h index dd8de82cf5b5..9fba9dd33544 100644 --- a/include/linux/t10-pi.h +++ b/include/linux/t10-pi.h @@ -4,6 +4,26 @@ #include #include +/* + * A T10 PI-capable target device can be formatted with different + * protection schemes. Currently 0 through 3 are defined: + * + * Type 0 is regular (unprotected) I/O + * + * Type 1 defines the contents of the guard and reference tags + * + * Type 2 defines the contents of the guard and reference tags and + * uses 32-byte commands to seed the latter + * + * Type 3 defines the contents of the guard tag only + */ +enum t10_dif_type { + T10_PI_TYPE0_PROTECTION = 0x0, + T10_PI_TYPE1_PROTECTION = 0x1, + T10_PI_TYPE2_PROTECTION = 0x2, + T10_PI_TYPE3_PROTECTION = 0x3, +}; + /* * T10 Protection Information tuple. */ From 38247feb60512a52c4f847933f8f931284dc21f4 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 15 Sep 2016 03:25:23 +0000 Subject: [PATCH 150/173] scsi: ibmvscsis: Fix error return code in ibmvscsis_probe() Fix to return error code -ENOMEM from the dma mapping error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index 4dd8e5effddb..38b086ee7d7e 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -3449,6 +3449,7 @@ static int ibmvscsis_probe(struct vio_dev *vdev, vscsi->map_ioba = dma_map_single(&vdev->dev, vscsi->map_buf, PAGE_SIZE, DMA_BIDIRECTIONAL); if (dma_mapping_error(&vdev->dev, vscsi->map_ioba)) { + rc = -ENOMEM; dev_err(&vscsi->dev, "probe: error mapping command buffer\n"); goto free_buf; } From 7bc2b55a5c030685b399bb65b6baa9ccc3d1f167 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Sep 2016 16:44:56 +0300 Subject: [PATCH 151/173] scsi: arcmsr: Buffer overflow in arcmsr_iop_message_xfer() We need to put an upper bound on "user_len" so the memcpy() doesn't overflow. Cc: Reported-by: Marco Grassi Signed-off-by: Dan Carpenter Reviewed-by: Tomas Henzl Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 7640498964a5..110eca9eaca0 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -2388,7 +2388,8 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, } case ARCMSR_MESSAGE_WRITE_WQBUFFER: { unsigned char *ver_addr; - int32_t user_len, cnt2end; + uint32_t user_len; + int32_t cnt2end; uint8_t *pQbuffer, *ptmpuserbuffer; ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC); if (!ver_addr) { @@ -2397,6 +2398,11 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, } ptmpuserbuffer = ver_addr; user_len = pcmdmessagefld->cmdmessage.Length; + if (user_len > ARCMSR_API_DATA_BUFLEN) { + retvalue = ARCMSR_MESSAGE_FAIL; + kfree(ver_addr); + goto message_out; + } memcpy(ptmpuserbuffer, pcmdmessagefld->messagedatabuffer, user_len); spin_lock_irqsave(&acb->wqbuffer_lock, flags); From 96c11dd2f0d76dc316d3810bfe824b12c70e4701 Mon Sep 17 00:00:00 2001 From: "Bryant G. Ly" Date: Fri, 16 Sep 2016 09:25:56 -0500 Subject: [PATCH 152/173] scsi: ibmvscsis: Fixed unused variable [mkp: applied by hand] Signed-off-by: Bryant G. Ly Reviewed-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index 38b086ee7d7e..642b739ad0da 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -3186,9 +3186,6 @@ static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg, vscsi->dds.window[LOCAL].liobn, server_ioba); } else { - /* write to client */ - struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf; - /* The h_copy_rdma will cause phyp, running in another * partition, to read memory, so we need to make sure * the data has been written out, hence these syncs. From abbada7175999fbd6500b8144e985b779588962f Mon Sep 17 00:00:00 2001 From: Mahesh Rajashekhara Date: Fri, 16 Sep 2016 14:54:23 -0500 Subject: [PATCH 153/173] scsi: hpsa: correct scsi 6byte lba calculation Missing 5 bits of byte 1 in the LBA issued by SML. Reported-by: Mahesh Rajashekhara Reviewed-by: Scott Teel Reviewed-by: Kevin Barnett Signed-off-by: Mahesh Rajashekhara Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index e5c02d7cb34b..261b9bba1f0f 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -4565,7 +4565,9 @@ static int fixup_ioaccel_cdb(u8 *cdb, int *cdb_len) case READ_6: case READ_12: if (*cdb_len == 6) { - block = get_unaligned_be16(&cdb[2]); + block = (((cdb[1] & 0x1F) << 16) | + (cdb[2] << 8) | + cdb[3]); block_cnt = cdb[4]; if (block_cnt == 0) block_cnt = 256; @@ -4725,9 +4727,11 @@ static void set_encrypt_ioaccel2(struct ctlr_info *h, */ switch (cmd->cmnd[0]) { /* Required? 6-byte cdbs eliminated by fixup_ioaccel_cdb */ - case WRITE_6: case READ_6: - first_block = get_unaligned_be16(&cmd->cmnd[2]); + case WRITE_6: + first_block = (((cmd->cmnd[1] & 0x1F) << 16) | + (cmd->cmnd[2] << 8) | + cmd->cmnd[3]); break; case WRITE_10: case READ_10: @@ -4977,7 +4981,9 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h, case WRITE_6: is_write = 1; case READ_6: - first_block = get_unaligned_be16(&cmd->cmnd[2]); + first_block = (((cmd->cmnd[1] & 0x1F) << 16) | + (cmd->cmnd[2] << 8) | + cmd->cmnd[3]); block_cnt = cmd->cmnd[4]; if (block_cnt == 0) block_cnt = 256; From e018ef572ba4ff17caa9e82d5e1b5cea0d76f903 Mon Sep 17 00:00:00 2001 From: kevin Barnett Date: Fri, 16 Sep 2016 15:01:51 -0500 Subject: [PATCH 154/173] scsi: smartpqi: raid bypass lba calculation fix In the ioaccel path, the calculation of the starting LBA for READ(6)/WRITE(6) SCSI commands does not take into account the most significant 5 bits of the LBA: it only uses the least significant 16 bits of the starting LBA. Reported-by: Mahesh Rajashekhara Signed-off-by: Kevin Barnett Signed-off-by: Don Brace Signed-off-by: Martin K. Petersen --- drivers/scsi/smartpqi/smartpqi_init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 52cfa268ca95..a535b2661f38 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -1954,7 +1954,8 @@ static int pqi_raid_bypass_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, is_write = true; /* fall through */ case READ_6: - first_block = (u64)get_unaligned_be16(&scmd->cmnd[2]); + first_block = (u64)(((scmd->cmnd[1] & 0x1f) << 16) | + (scmd->cmnd[2] << 8) | scmd->cmnd[3]); block_cnt = (u32)scmd->cmnd[4]; if (block_cnt == 0) block_cnt = 256; From 7b3871fd17a2092b21e44d614706bcb495389559 Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 16 Sep 2016 16:51:36 -0500 Subject: [PATCH 155/173] scsi: ipr: Don't log unnecessary 9084 error details A 9084 error gets logged by the ipr adapter when adapter raw mode gets enabled. A bunch of unformatted hex data also gets logged for this error, which is of little use, so let's avoid logging it by default in order to avoid the log getting polluted with useless data. Signed-off-by: Brian King Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Martin K. Petersen --- drivers/scsi/ipr.c | 6 +++--- drivers/scsi/ipr.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 5ecc32cecd10..762627c848bd 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -493,15 +493,15 @@ struct ipr_error_table_t ipr_error_table[] = { "9072: Link not operational transition"}, {0x066B8200, 0, IPR_DEFAULT_LOG_LEVEL, "9032: Array exposed but still protected"}, - {0x066B8300, 0, IPR_DEFAULT_LOG_LEVEL + 1, + {0x066B8300, 0, IPR_DEBUG_LOG_LEVEL, "70DD: Device forced failed by disrupt device command"}, {0x066B9100, 0, IPR_DEFAULT_LOG_LEVEL, "4061: Multipath redundancy level got better"}, {0x066B9200, 0, IPR_DEFAULT_LOG_LEVEL, "4060: Multipath redundancy level got worse"}, - {0x06808100, 0, IPR_DEFAULT_LOG_LEVEL, + {0x06808100, 0, IPR_DEBUG_LOG_LEVEL, "9083: Device raw mode enabled"}, - {0x06808200, 0, IPR_DEFAULT_LOG_LEVEL, + {0x06808200, 0, IPR_DEBUG_LOG_LEVEL, "9084: Device raw mode disabled"}, {0x07270000, 0, 0, "Failure due to other device"}, diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 4dbeaafa0ba2..8995053d01b3 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1506,6 +1506,7 @@ struct ipr_ioa_cfg { u8 log_level; #define IPR_MAX_LOG_LEVEL 4 #define IPR_DEFAULT_LOG_LEVEL 2 +#define IPR_DEBUG_LOG_LEVEL 3 #define IPR_NUM_TRACE_INDEX_BITS 8 #define IPR_NUM_TRACE_ENTRIES (1 << IPR_NUM_TRACE_INDEX_BITS) From 87adbe08b644cd4dfa078ec1a09be7d2b24ce97d Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 16 Sep 2016 16:51:37 -0500 Subject: [PATCH 156/173] scsi: ipr: Remove redundant messages at adapter init time Whenever multiple HRRQs are enabled, which is the default setting now, we end up seeing the following message logged prior to initialization of each HRRQ: Starting IOA initialization sequence This results in 16 of these messages on most adapters, which serves little purpose. Change to just log this once. Signed-off-by: Brian King Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Martin K. Petersen --- drivers/scsi/ipr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 762627c848bd..1b21c6331c56 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -8049,7 +8049,8 @@ static int ipr_ioafp_identify_hrrq(struct ipr_cmnd *ipr_cmd) ENTER; ipr_cmd->job_step = ipr_ioafp_std_inquiry; - dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n"); + if (ioa_cfg->identify_hrrq_index == 0) + dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n"); if (ioa_cfg->identify_hrrq_index < ioa_cfg->hrrq_num) { hrrq = &ioa_cfg->hrrq[ioa_cfg->identify_hrrq_index]; From 2f3e77323d3159cb6c2a002fa6c58ba22e7c837e Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Sun, 18 Sep 2016 20:07:59 +0800 Subject: [PATCH 157/173] scsi: megaraid_sas: clean function declarations in megaraid_sas_base.c up We get a few warnings when building kernel with W=1: drivers/scsi/megaraid/megaraid_sas_fusion.c:281:1: warning: no previous prototype for 'megasas_free_cmds_fusion' [-Wmissing-prototypes] drivers/scsi/megaraid/megaraid_sas_fusion.c:714:1: warning: no previous prototype for 'megasas_ioc_init_fusion' [-Wmissing-prototypes] .... In fact, these functions are declared in drivers/scsi/megaraid/megaraid_sas_base.c, but should be declared in a header file, thus can be recognized in other file. So this patch adds the declarations into drivers/scsi/megaraid/megaraid_sas_fusion.h. Signed-off-by: Baoyou Xie Acked-by: Sumit Saxena Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_base.c | 13 ------------- drivers/scsi/megaraid/megaraid_sas_fusion.h | 9 +++++++++ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 255f79ea9813..c3efcc725540 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -189,25 +189,12 @@ u32 megasas_build_and_issue_cmd(struct megasas_instance *instance, struct scsi_cmnd *scmd); static void megasas_complete_cmd_dpc(unsigned long instance_addr); -void -megasas_release_fusion(struct megasas_instance *instance); -int -megasas_ioc_init_fusion(struct megasas_instance *instance); -void -megasas_free_cmds_fusion(struct megasas_instance *instance); -u8 -megasas_get_map_info(struct megasas_instance *instance); -int -megasas_sync_map_info(struct megasas_instance *instance); int wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd, int seconds); -void megasas_reset_reply_desc(struct megasas_instance *instance); void megasas_fusion_ocr_wq(struct work_struct *work); static int megasas_get_ld_vf_affiliation(struct megasas_instance *instance, int initial); -int megasas_check_mpio_paths(struct megasas_instance *instance, - struct scsi_cmnd *scmd); int megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd) diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 80eaee22f5bc..e3bee04c1eb1 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -991,5 +991,14 @@ union desc_value { } u; }; +void megasas_free_cmds_fusion(struct megasas_instance *instance); +int megasas_ioc_init_fusion(struct megasas_instance *instance); +u8 megasas_get_map_info(struct megasas_instance *instance); +int megasas_sync_map_info(struct megasas_instance *instance); +void megasas_release_fusion(struct megasas_instance *instance); +void megasas_reset_reply_desc(struct megasas_instance *instance); +int megasas_check_mpio_paths(struct megasas_instance *instance, + struct scsi_cmnd *scmd); +void megasas_fusion_ocr_wq(struct work_struct *work); #endif /* _MEGARAID_SAS_FUSION_H_ */ From 07d0e9a847401ffd2f09bd450d41644cd090e81d Mon Sep 17 00:00:00 2001 From: Brian King Date: Mon, 19 Sep 2016 08:59:19 -0500 Subject: [PATCH 158/173] scsi: ibmvfc: Fix I/O hang when port is not mapped If a VFC port gets unmapped in the VIOS, it may not respond with a CRQ init complete following H_REG_CRQ. If this occurs, we can end up having called scsi_block_requests and not a resulting unblock until the init complete happens, which may never occur, and we end up hanging I/O requests. This patch ensures the host action stay set to IBMVFC_HOST_ACTION_TGT_DEL so we move all rports into devloss state and unblock unless we receive an init complete. Cc: Signed-off-by: Brian King Acked-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi/ibmvfc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 56b9f452425b..7e487c78279c 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -721,7 +721,6 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) spin_lock_irqsave(vhost->host->host_lock, flags); vhost->state = IBMVFC_NO_CRQ; vhost->logged_in = 0; - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); /* Clean out the queue */ memset(crq->msgs, 0, PAGE_SIZE); From 8794ee0c1d4ce35b8b8e4e8cdc80bba6e42206eb Mon Sep 17 00:00:00 2001 From: Kiwoong Kim Date: Fri, 9 Sep 2016 08:22:22 +0900 Subject: [PATCH 159/173] scsi: ufs: Get a TM service response from the correct offset When any UFS host controller receives a TM(Task Management) response from a UFS device, UFS driver has been recognize like receiving a message of "Task Management Function Complete"(00h) in all cases, so far. That means there is no pending task for a tag of the TM request sent before in the UFS device. That's because the byte offset 6 in TM response which has been used to get a TM service response so far represents just whether or not a TM transmission passes. Regarding UFS spec, the correct byte offset to get TM service response is 15, not 6. I tested that UFS driver responds properly for the TM response from a UFS device with an reference board with exynos8890, as follow: No pending task -> Task Management Function Complete (00h) Pending task -> Task Management Function Succeeded (08h) [mkp: applied by hand] Signed-off-by: Kiwoong Kim Signed-off-by: HeonGwang Chu Tested-by: : Kiwoong Kim Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs.h | 1 + drivers/scsi/ufs/ufshcd.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index b291fa6ed2ad..845b874e2977 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -327,6 +327,7 @@ enum { MASK_QUERY_DATA_SEG_LEN = 0xFFFF, MASK_RSP_UPIU_DATA_SEG_LEN = 0xFFFF, MASK_RSP_EXCEPTION_EVENT = 0x10000, + MASK_TM_SERVICE_RESP = 0xFF, }; /* Task management service response */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 66f0eb80b93e..37f3c51e9d92 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3364,8 +3364,8 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) if (ocs_value == OCS_SUCCESS) { task_rsp_upiup = (struct utp_upiu_task_rsp *) task_req_descp[index].task_rsp_upiu; - task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); - task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); + task_result = be32_to_cpu(task_rsp_upiup->output_param1); + task_result = task_result & MASK_TM_SERVICE_RESP; if (resp) *resp = (u8)task_result; } else { From b32ece0ff70c30e59f16e9a8ffd05acc27298ba3 Mon Sep 17 00:00:00 2001 From: Don Brace Date: Tue, 20 Sep 2016 15:42:30 -0500 Subject: [PATCH 160/173] scsi: hpsa: correct call to hpsa_do_reset calling fill_cmd() using a MACRO definition not handled in switch statement causes BUG() to be called. Signed-off-by: Don Brace Reviewed-by: Scott Teel Reviewed-by: Kevin Barnett Reviewed-by: Tomas Henzl Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 2 +- drivers/scsi/hpsa.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 261b9bba1f0f..d007ec18179a 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -6017,7 +6017,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h, "Reset as abort: Resetting physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", psa[0], psa[1], psa[2], psa[3], psa[4], psa[5], psa[6], psa[7]); - rc = hpsa_do_reset(h, dev, psa, HPSA_RESET_TYPE_TARGET, reply_queue); + rc = hpsa_do_reset(h, dev, psa, HPSA_PHYS_TARGET_RESET, reply_queue); if (rc != 0) { dev_warn(&h->pdev->dev, "Reset as abort: Failed on physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index a1487e67f7a1..82cdfad874f3 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -312,7 +312,6 @@ struct offline_device_entry { #define HPSA_DEVICE_RESET_MSG 1 #define HPSA_RESET_TYPE_CONTROLLER 0x00 #define HPSA_RESET_TYPE_BUS 0x01 -#define HPSA_RESET_TYPE_TARGET 0x03 #define HPSA_RESET_TYPE_LUN 0x04 #define HPSA_PHYS_TARGET_RESET 0x99 /* not defined by cciss spec */ #define HPSA_MSG_SEND_RETRY_LIMIT 10 From bd4b3e5c8adf2b9f601b46b917afac555be05688 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Sun, 25 Sep 2016 13:44:55 +0800 Subject: [PATCH 161/173] scsi: lpfc: Mark symbols static where possible We get a few warnings when building kernel with W=1: drivers/scsi/lpfc/lpfc_sli.c:5693:1: warning: no previous prototype for 'lpfc_set_features' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_sli.c:8972:1: warning: no previous prototype for 'lpfc_sli_calc_ring' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_els.c:4621:1: warning: no previous prototype for 'lpfc_rdp_res_link_service' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_els.c:4633:1: warning: no previous prototype for 'lpfc_rdp_res_sfp_desc' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_els.c:4698:1: warning: no previous prototype for 'lpfc_rdp_res_link_error' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_els.c:4727:1: warning: no previous prototype for 'lpfc_rdp_res_bbc_desc' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_els.c:4752:1: warning: no previous prototype for 'lpfc_rdp_res_oed_temp_desc' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_els.c:4780:1: warning: no previous prototype for 'lpfc_rdp_res_oed_voltage_desc' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_els.c:4809:1: warning: no previous prototype for 'lpfc_rdp_res_oed_txbias_desc' [-Wmissing-prototypes] drivers/scsi/lpfc/lpfc_els.c:4838:1: warning: no previous prototype for 'lpfc_rdp_res_oed_txpower_desc' [-Wmissing-prototypes] .... In fact, these functions are only used in the file in which they are declared and don't need a declaration, but can be made static. So this patch marks these functions with 'static'. Signed-off-by: Baoyou Xie Acked-by: Arnd Bergmann Acked-by: Dick Kennedy Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_ct.c | 82 +++++++++++++++++------------------ drivers/scsi/lpfc/lpfc_els.c | 34 +++++++-------- drivers/scsi/lpfc/lpfc_mbox.c | 4 +- drivers/scsi/lpfc/lpfc_sli.c | 4 +- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 63e48d4277b0..4ac03b16d17f 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1535,7 +1535,7 @@ lpfc_fdmi_num_disc_check(struct lpfc_vport *vport) } /* Routines for all individual HBA attributes */ -int +static int lpfc_fdmi_hba_attr_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { struct lpfc_fdmi_attr_entry *ae; @@ -1551,7 +1551,7 @@ lpfc_fdmi_hba_attr_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) ad->AttrType = cpu_to_be16(RHBA_NODENAME); return size; } -int +static int lpfc_fdmi_hba_attr_manufacturer(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1573,7 +1573,7 @@ lpfc_fdmi_hba_attr_manufacturer(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_sn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { struct lpfc_hba *phba = vport->phba; @@ -1594,7 +1594,7 @@ lpfc_fdmi_hba_attr_sn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) return size; } -int +static int lpfc_fdmi_hba_attr_model(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1615,7 +1615,7 @@ lpfc_fdmi_hba_attr_model(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_description(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1637,7 +1637,7 @@ lpfc_fdmi_hba_attr_description(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_hdw_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1669,7 +1669,7 @@ lpfc_fdmi_hba_attr_hdw_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_drvr_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1690,7 +1690,7 @@ lpfc_fdmi_hba_attr_drvr_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_rom_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1715,7 +1715,7 @@ lpfc_fdmi_hba_attr_rom_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_fmw_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1736,7 +1736,7 @@ lpfc_fdmi_hba_attr_fmw_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_os_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1759,7 +1759,7 @@ lpfc_fdmi_hba_attr_os_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_ct_len(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1775,7 +1775,7 @@ lpfc_fdmi_hba_attr_ct_len(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_symbolic_name(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1794,7 +1794,7 @@ lpfc_fdmi_hba_attr_symbolic_name(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_vendor_info(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1811,7 +1811,7 @@ lpfc_fdmi_hba_attr_vendor_info(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_num_ports(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1828,7 +1828,7 @@ lpfc_fdmi_hba_attr_num_ports(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_fabric_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1846,7 +1846,7 @@ lpfc_fdmi_hba_attr_fabric_wwnn(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_bios_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1867,7 +1867,7 @@ lpfc_fdmi_hba_attr_bios_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_bios_state(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1884,7 +1884,7 @@ lpfc_fdmi_hba_attr_bios_state(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_vendor_id(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1906,7 +1906,7 @@ lpfc_fdmi_hba_attr_vendor_id(struct lpfc_vport *vport, } /* Routines for all individual PORT attributes */ -int +static int lpfc_fdmi_port_attr_fc4type(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1925,7 +1925,7 @@ lpfc_fdmi_port_attr_fc4type(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_support_speed(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1975,7 +1975,7 @@ lpfc_fdmi_port_attr_support_speed(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_speed(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2039,7 +2039,7 @@ lpfc_fdmi_port_attr_speed(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_max_frame(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2059,7 +2059,7 @@ lpfc_fdmi_port_attr_max_frame(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_os_devname(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2081,7 +2081,7 @@ lpfc_fdmi_port_attr_os_devname(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_host_name(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2102,7 +2102,7 @@ lpfc_fdmi_port_attr_host_name(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2120,7 +2120,7 @@ lpfc_fdmi_port_attr_wwnn(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_wwpn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2138,7 +2138,7 @@ lpfc_fdmi_port_attr_wwpn(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_symbolic_name(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2156,7 +2156,7 @@ lpfc_fdmi_port_attr_symbolic_name(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_port_type(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2175,7 +2175,7 @@ lpfc_fdmi_port_attr_port_type(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_class(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2190,7 +2190,7 @@ lpfc_fdmi_port_attr_class(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_fabric_wwpn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2208,7 +2208,7 @@ lpfc_fdmi_port_attr_fabric_wwpn(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_active_fc4type(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2227,7 +2227,7 @@ lpfc_fdmi_port_attr_active_fc4type(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_port_state(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2243,7 +2243,7 @@ lpfc_fdmi_port_attr_port_state(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_num_disc(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2259,7 +2259,7 @@ lpfc_fdmi_port_attr_num_disc(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_nportid(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2274,7 +2274,7 @@ lpfc_fdmi_port_attr_nportid(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_service(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2295,7 +2295,7 @@ lpfc_fdmi_smart_attr_service(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_guid(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2316,7 +2316,7 @@ lpfc_fdmi_smart_attr_guid(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_version(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2337,7 +2337,7 @@ lpfc_fdmi_smart_attr_version(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_model(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2358,7 +2358,7 @@ lpfc_fdmi_smart_attr_model(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_port_info(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2378,7 +2378,7 @@ lpfc_fdmi_smart_attr_port_info(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_qos(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2393,7 +2393,7 @@ lpfc_fdmi_smart_attr_qos(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_security(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index c0af32f24954..b7d54bfb1df9 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -4617,7 +4617,7 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport) return sentplogi; } -uint32_t +static uint32_t lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc, uint32_t word0) { @@ -4629,7 +4629,7 @@ lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc, return sizeof(struct fc_rdp_link_service_desc); } -uint32_t +static uint32_t lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc, uint8_t *page_a0, uint8_t *page_a2) { @@ -4694,7 +4694,7 @@ lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc, return sizeof(struct fc_rdp_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc, READ_LNK_VAR *stat) { @@ -4723,7 +4723,7 @@ lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc, return sizeof(struct fc_rdp_link_error_status_desc); } -uint32_t +static uint32_t lpfc_rdp_res_bbc_desc(struct fc_rdp_bbc_desc *desc, READ_LNK_VAR *stat, struct lpfc_vport *vport) { @@ -4748,7 +4748,7 @@ lpfc_rdp_res_bbc_desc(struct fc_rdp_bbc_desc *desc, READ_LNK_VAR *stat, return sizeof(struct fc_rdp_bbc_desc); } -uint32_t +static uint32_t lpfc_rdp_res_oed_temp_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) { @@ -4776,7 +4776,7 @@ lpfc_rdp_res_oed_temp_desc(struct lpfc_hba *phba, return sizeof(struct fc_rdp_oed_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_oed_voltage_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) @@ -4805,7 +4805,7 @@ lpfc_rdp_res_oed_voltage_desc(struct lpfc_hba *phba, return sizeof(struct fc_rdp_oed_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_oed_txbias_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) @@ -4834,7 +4834,7 @@ lpfc_rdp_res_oed_txbias_desc(struct lpfc_hba *phba, return sizeof(struct fc_rdp_oed_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_oed_txpower_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) @@ -4864,7 +4864,7 @@ lpfc_rdp_res_oed_txpower_desc(struct lpfc_hba *phba, } -uint32_t +static uint32_t lpfc_rdp_res_oed_rxpower_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) @@ -4893,7 +4893,7 @@ lpfc_rdp_res_oed_rxpower_desc(struct lpfc_hba *phba, return sizeof(struct fc_rdp_oed_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_opd_desc(struct fc_rdp_opd_sfp_desc *desc, uint8_t *page_a0, struct lpfc_vport *vport) { @@ -4907,7 +4907,7 @@ lpfc_rdp_res_opd_desc(struct fc_rdp_opd_sfp_desc *desc, return sizeof(struct fc_rdp_opd_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_fec_desc(struct fc_fec_rdp_desc *desc, READ_LNK_VAR *stat) { if (bf_get(lpfc_read_link_stat_gec2, stat) == 0) @@ -4924,7 +4924,7 @@ lpfc_rdp_res_fec_desc(struct fc_fec_rdp_desc *desc, READ_LNK_VAR *stat) return sizeof(struct fc_fec_rdp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba) { uint16_t rdp_cap = 0; @@ -4986,7 +4986,7 @@ lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba) return sizeof(struct fc_rdp_port_speed_desc); } -uint32_t +static uint32_t lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc, struct lpfc_hba *phba) { @@ -5003,7 +5003,7 @@ lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc, return sizeof(struct fc_rdp_port_name_desc); } -uint32_t +static uint32_t lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc, struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { @@ -5027,7 +5027,7 @@ lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc, return sizeof(struct fc_rdp_port_name_desc); } -void +static void lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context, int status) { @@ -5165,7 +5165,7 @@ free_rdp_context: kfree(rdp_context); } -int +static int lpfc_get_rdp_info(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context) { LPFC_MBOXQ_t *mbox = NULL; @@ -7995,7 +7995,7 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, } } -void +static void lpfc_start_fdmi(struct lpfc_vport *vport) { struct lpfc_hba *phba = vport->phba; diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index 12dbe99ccc50..b234c50c255f 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -2260,7 +2260,7 @@ lpfc_sli4_dump_cfg_rg23(struct lpfc_hba *phba, struct lpfcMboxq *mbox) return 0; } -void +static void lpfc_mbx_cmpl_rdp_link_stat(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) { MAILBOX_t *mb; @@ -2281,7 +2281,7 @@ mbx_failed: rdp_context->cmpl(phba, rdp_context, rc); } -void +static void lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) { struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) mbox->context1; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 7080ce2920fd..c5326055beee 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -5689,7 +5689,7 @@ lpfc_sli4_dealloc_extent(struct lpfc_hba *phba, uint16_t type) return rc; } -void +static void lpfc_set_features(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox, uint32_t feature) { @@ -8968,7 +8968,7 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) * Since ABORTS must go on the same WQ of the command they are * aborting, we use command's fcp_wqidx. */ -int +static int lpfc_sli_calc_ring(struct lpfc_hba *phba, uint32_t ring_number, struct lpfc_iocbq *piocb) { From 086acff2cf8a6a41f533e5da5f51923db5f68df4 Mon Sep 17 00:00:00 2001 From: "tang.junhui" Date: Sun, 18 Sep 2016 18:05:11 +0800 Subject: [PATCH 162/173] scsi: scsi_dh_alua: Fix memory leak in alua_rtpg() buff should be freed before returning with SCSI_DH_RETRY in alua_rtpg(). Signed-off-by: tang.junhui Signed-off-by: Martin K. Petersen --- drivers/scsi/device_handler/scsi_dh_alua.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 752b5c9d1ab2..241829e59668 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -583,6 +583,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) sdev_printk(KERN_ERR, sdev, "%s: rtpg retry\n", ALUA_DH_NAME); scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr); + kfree(buff); return err; } sdev_printk(KERN_ERR, sdev, "%s: rtpg failed\n", From 9b3a34fb2125141720515b79ed2228545645a7bc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 19 Sep 2016 08:50:47 -0700 Subject: [PATCH 163/173] scsi: wd7000: remove from tree The driver has not seen any maintainer activity or other work that wasn't tree wide conversion or clenaups in the entire history of the git tree. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinicke Signed-off-by: Martin K. Petersen --- Documentation/scsi/scsi-parameters.txt | 3 - MAINTAINERS | 6 - drivers/scsi/Kconfig | 12 - drivers/scsi/Makefile | 1 - drivers/scsi/wd7000.c | 1657 ------------------------ 5 files changed, 1679 deletions(-) delete mode 100644 drivers/scsi/wd7000.c diff --git a/Documentation/scsi/scsi-parameters.txt b/Documentation/scsi/scsi-parameters.txt index d5ae6ced6be3..5a5c6088a6ec 100644 --- a/Documentation/scsi/scsi-parameters.txt +++ b/Documentation/scsi/scsi-parameters.txt @@ -131,6 +131,3 @@ parameters may be changed at runtime by the command wd33c93= [HW,SCSI] See header of drivers/scsi/wd33c93.c. - - wd7000= [HW,SCSI] - See header of drivers/scsi/wd7000.c. diff --git a/MAINTAINERS b/MAINTAINERS index dd4966d30a27..fdcce6b52cba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12699,12 +12699,6 @@ F: drivers/watchdog/ F: include/linux/watchdog.h F: include/uapi/linux/watchdog.h -WD7000 SCSI DRIVER -M: Miroslav Zagorac -L: linux-scsi@vger.kernel.org -S: Maintained -F: drivers/scsi/wd7000.c - WIIMOTE HID DRIVER M: David Herrmann L: linux-input@vger.kernel.org diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 75de1dc725a1..15c6e9f2162d 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -396,18 +396,6 @@ config SCSI_3W_SAS Please read the comments at the top of . -config SCSI_7000FASST - tristate "7000FASST SCSI support" - depends on ISA && SCSI && ISA_DMA_API - select CHECK_SIGNATURE - help - This driver supports the Western Digital 7000 SCSI host adapter - family. Some information is in the source: - . - - To compile this driver as a module, choose M here: the - module will be called wd7000. - config SCSI_ACARD tristate "ACARD SCSI support" depends on PCI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index fc0d9b8f2bdf..d870cc51d0bb 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -99,7 +99,6 @@ obj-$(CONFIG_SCSI_DTC3280) += dtc.o obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/ obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o -obj-$(CONFIG_SCSI_7000FASST) += wd7000.o obj-$(CONFIG_SCSI_EATA) += eata.o obj-$(CONFIG_SCSI_DC395x) += dc395x.o obj-$(CONFIG_SCSI_AM53C974) += esp_scsi.o am53c974.o diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c deleted file mode 100644 index 409f959845c4..000000000000 --- a/drivers/scsi/wd7000.c +++ /dev/null @@ -1,1657 +0,0 @@ -/* $Id: $ - * linux/drivers/scsi/wd7000.c - * - * Copyright (C) 1992 Thomas Wuensche - * closely related to the aha1542 driver from Tommy Thorn - * ( as close as different hardware allows on a lowlevel-driver :-) ) - * - * Revised (and renamed) by John Boyd to - * accommodate Eric Youngdale's modifications to scsi.c. Nov 1992. - * - * Additional changes to support scatter/gather. Dec. 1992. tw/jb - * - * No longer tries to reset SCSI bus at boot (it wasn't working anyway). - * Rewritten to support multiple host adapters. - * Miscellaneous cleanup. - * So far, still doesn't do reset or abort correctly, since I have no idea - * how to do them with this board (8^(. Jan 1994 jb - * - * This driver now supports both of the two standard configurations (per - * the 3.36 Owner's Manual, my latest reference) by the same method as - * before; namely, by looking for a BIOS signature. Thus, the location of - * the BIOS signature determines the board configuration. Until I have - * time to do something more flexible, users should stick to one of the - * following: - * - * Standard configuration for single-adapter systems: - * - BIOS at CE00h - * - I/O base address 350h - * - IRQ level 15 - * - DMA channel 6 - * Standard configuration for a second adapter in a system: - * - BIOS at C800h - * - I/O base address 330h - * - IRQ level 11 - * - DMA channel 5 - * - * Anyone who can recompile the kernel is welcome to add others as need - * arises, but unpredictable results may occur if there are conflicts. - * In any event, if there are multiple adapters in a system, they MUST - * use different I/O bases, IRQ levels, and DMA channels, since they will be - * indistinguishable (and in direct conflict) otherwise. - * - * As a point of information, the NO_OP command toggles the CMD_RDY bit - * of the status port, and this fact could be used as a test for the I/O - * base address (or more generally, board detection). There is an interrupt - * status port, so IRQ probing could also be done. I suppose the full - * DMA diagnostic could be used to detect the DMA channel being used. I - * haven't done any of this, though, because I think there's too much of - * a chance that such explorations could be destructive, if some other - * board's resources are used inadvertently. So, call me a wimp, but I - * don't want to try it. The only kind of exploration I trust is memory - * exploration, since it's more certain that reading memory won't be - * destructive. - * - * More to my liking would be a LILO boot command line specification, such - * as is used by the aha152x driver (and possibly others). I'll look into - * it, as I have time... - * - * I get mail occasionally from people who either are using or are - * considering using a WD7000 with Linux. There is a variety of - * nomenclature describing WD7000's. To the best of my knowledge, the - * following is a brief summary (from an old WD doc - I don't work for - * them or anything like that): - * - * WD7000-FASST2: This is a WD7000 board with the real-mode SST ROM BIOS - * installed. Last I heard, the BIOS was actually done by Columbia - * Data Products. The BIOS is only used by this driver (and thus - * by Linux) to identify the board; none of it can be executed under - * Linux. - * - * WD7000-ASC: This is the original adapter board, with or without BIOS. - * The board uses a WD33C93 or WD33C93A SBIC, which in turn is - * controlled by an onboard Z80 processor. The board interface - * visible to the host CPU is defined effectively by the Z80's - * firmware, and it is this firmware's revision level that is - * determined and reported by this driver. (The version of the - * on-board BIOS is of no interest whatsoever.) The host CPU has - * no access to the SBIC; hence the fact that it is a WD33C93 is - * also of no interest to this driver. - * - * WD7000-AX: - * WD7000-MX: - * WD7000-EX: These are newer versions of the WD7000-ASC. The -ASC is - * largely built from discrete components; these boards use more - * integration. The -AX is an ISA bus board (like the -ASC), - * the -MX is an MCA (i.e., PS/2) bus board), and the -EX is an - * EISA bus board. - * - * At the time of my documentation, the -?X boards were "future" products, - * and were not yet available. However, I vaguely recall that Thomas - * Wuensche had an -AX, so I believe at least it is supported by this - * driver. I have no personal knowledge of either -MX or -EX boards. - * - * P.S. Just recently, I've discovered (directly from WD and Future - * Domain) that all but the WD7000-EX have been out of production for - * two years now. FD has production rights to the 7000-EX, and are - * producing it under a new name, and with a new BIOS. If anyone has - * one of the FD boards, it would be nice to come up with a signature - * for it. - * J.B. Jan 1994. - * - * - * Revisions by Miroslav Zagorac - * - * 08/24/1996. - * - * Enhancement for wd7000_detect function has been made, so you don't have - * to enter BIOS ROM address in initialisation data (see struct Config). - * We cannot detect IRQ, DMA and I/O base address for now, so we have to - * enter them as arguments while wd_7000 is detected. If someone has IRQ, - * DMA or I/O base address set to some other value, he can enter them in - * configuration without any problem. Also I wrote a function wd7000_setup, - * so now you can enter WD-7000 definition as kernel arguments, - * as in lilo.conf: - * - * append="wd7000=IRQ,DMA,IO" - * - * PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize - * adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is - * useless for Linux. B^) - * - * - * 09/06/1996. - * - * Autodetecting of I/O base address from wd7000_detect function is removed, - * some little bugs removed, etc... - * - * Thanks to Roger Scott for driver debugging. - * - * 06/07/1997 - * - * Added support for /proc file system (/proc/scsi/wd7000/[0...] files). - * Now, driver can handle hard disks with capacity >1GB. - * - * 01/15/1998 - * - * Added support for BUS_ON and BUS_OFF parameters in config line. - * Miscellaneous cleanup. - * - * 03/01/1998 - * - * WD7000 driver now work on kernels >= 2.1.x - * - * - * 12/31/2001 - Arnaldo Carvalho de Melo - * - * use host->host_lock, not io_request_lock, cleanups - * - * 2002/10/04 - Alan Cox - * - * Use dev_id for interrupts, kill __func__ pasting - * Add a lock for the scb pool, clean up all other cli/sti usage stuff - * Use the adapter lock for the other places we had the cli's - * - * 2002/10/06 - Alan Cox - * - * Switch to new style error handling - * Clean up delay to udelay, and yielding sleeps - * Make host reset actually reset the card - * Make everything static - * - * 2003/02/12 - Christoph Hellwig - * - * Cleaned up host template definition - * Removed now obsolete wd7000.h - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - - -#undef WD7000_DEBUG /* general debug */ -#ifdef WD7000_DEBUG -#define dprintk printk -#else -#define dprintk no_printk -#endif - -/* - * Mailbox structure sizes. - * I prefer to keep the number of ICMBs much larger than the number of - * OGMBs. OGMBs are used very quickly by the driver to start one or - * more commands, while ICMBs are used by the host adapter per command. - */ -#define OGMB_CNT 16 -#define ICMB_CNT 32 - -/* - * Scb's are shared by all active adapters. So, if they all become busy, - * callers may be made to wait in alloc_scbs for them to free. That can - * be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q. If you'd - * rather conserve memory, use a smaller number (> 0, of course) - things - * will should still work OK. - */ -#define MAX_SCBS 32 - -/* - * In this version, sg_tablesize now defaults to WD7000_SG, and will - * be set to SG_NONE for older boards. This is the reverse of the - * previous default, and was changed so that the driver-level - * scsi_host_template would reflect the driver's support for scatter/ - * gather. - * - * Also, it has been reported that boards at Revision 6 support scatter/ - * gather, so the new definition of an "older" board has been changed - * accordingly. - */ -#define WD7000_Q 16 -#define WD7000_SG 16 - - -/* - * WD7000-specific mailbox structure - * - */ -typedef volatile struct mailbox { - unchar status; - unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ -} Mailbox; - -/* - * This structure should contain all per-adapter global data. I.e., any - * new global per-adapter data should put in here. - */ -typedef struct adapter { - struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ - int iobase; /* This adapter's I/O base address */ - int irq; /* This adapter's IRQ level */ - int dma; /* This adapter's DMA channel */ - int int_counter; /* This adapter's interrupt counter */ - int bus_on; /* This adapter's BUS_ON time */ - int bus_off; /* This adapter's BUS_OFF time */ - struct { /* This adapter's mailboxes */ - Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ - Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ - } mb; - int next_ogmb; /* to reduce contention at mailboxes */ - unchar control; /* shadows CONTROL port value */ - unchar rev1, rev2; /* filled in by wd7000_revision */ -} Adapter; - -/* - * (linear) base address for ROM BIOS - */ -static const long wd7000_biosaddr[] = { - 0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000, - 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000 -}; -#define NUM_ADDRS ARRAY_SIZE(wd7000_biosaddr) - -static const unsigned short wd7000_iobase[] = { - 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338, - 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378, - 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8, - 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8 -}; -#define NUM_IOPORTS ARRAY_SIZE(wd7000_iobase) - -static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 }; -#define NUM_IRQS ARRAY_SIZE(wd7000_irq) - -static const short wd7000_dma[] = { 5, 6, 7 }; -#define NUM_DMAS ARRAY_SIZE(wd7000_dma) - -/* - * The following is set up by wd7000_detect, and used thereafter for - * proc and other global ookups - */ - -#define UNITS 8 -static struct Scsi_Host *wd7000_host[UNITS]; - -#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ -#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ - -/* - * Standard Adapter Configurations - used by wd7000_detect - */ -typedef struct { - short irq; /* IRQ level */ - short dma; /* DMA channel */ - unsigned iobase; /* I/O base address */ - short bus_on; /* Time that WD7000 spends on the AT-bus when */ - /* transferring data. BIOS default is 8000ns. */ - short bus_off; /* Time that WD7000 spends OFF THE BUS after */ - /* while it is transferring data. */ - /* BIOS default is 1875ns */ -} Config; - -/* - * Add here your configuration... - */ -static Config configs[] = { - {15, 6, 0x350, BUS_ON, BUS_OFF}, /* defaults for single adapter */ - {11, 5, 0x320, BUS_ON, BUS_OFF}, /* defaults for second adapter */ - {7, 6, 0x350, BUS_ON, BUS_OFF}, /* My configuration (Zaga) */ - {-1, -1, 0x0, BUS_ON, BUS_OFF} /* Empty slot */ -}; -#define NUM_CONFIGS ARRAY_SIZE(configs) - -/* - * The following list defines strings to look for in the BIOS that identify - * it as the WD7000-FASST2 SST BIOS. I suspect that something should be - * added for the Future Domain version. - */ -typedef struct signature { - const char *sig; /* String to look for */ - unsigned long ofs; /* offset from BIOS base address */ - unsigned len; /* length of string */ -} Signature; - -static const Signature signatures[] = { - {"SSTBIOS", 0x0000d, 7} /* "SSTBIOS" @ offset 0x0000d */ -}; -#define NUM_SIGNATURES ARRAY_SIZE(signatures) - - -/* - * I/O Port Offsets and Bit Definitions - * 4 addresses are used. Those not defined here are reserved. - */ -#define ASC_STAT 0 /* Status, Read */ -#define ASC_COMMAND 0 /* Command, Write */ -#define ASC_INTR_STAT 1 /* Interrupt Status, Read */ -#define ASC_INTR_ACK 1 /* Acknowledge, Write */ -#define ASC_CONTROL 2 /* Control, Write */ - -/* - * ASC Status Port - */ -#define INT_IM 0x80 /* Interrupt Image Flag */ -#define CMD_RDY 0x40 /* Command Port Ready */ -#define CMD_REJ 0x20 /* Command Port Byte Rejected */ -#define ASC_INIT 0x10 /* ASC Initialized Flag */ -#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */ - -/* - * COMMAND opcodes - * - * Unfortunately, I have no idea how to properly use some of these commands, - * as the OEM manual does not make it clear. I have not been able to use - * enable/disable unsolicited interrupts or the reset commands with any - * discernible effect whatsoever. I think they may be related to certain - * ICB commands, but again, the OEM manual doesn't make that clear. - */ -#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ -#define INITIALIZATION 1 /* initialization (10 bytes) */ -#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ -#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ -#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ -#define SOFT_RESET 5 /* SCSI bus soft reset */ -#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ -#define START_OGMB 0x80 /* start command in OGMB (n) */ -#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ - /* where (n) = lower 6 bits */ -/* - * For INITIALIZATION: - */ -typedef struct initCmd { - unchar op; /* command opcode (= 1) */ - unchar ID; /* Adapter's SCSI ID */ - unchar bus_on; /* Bus on time, x 125ns (see below) */ - unchar bus_off; /* Bus off time, "" "" */ - unchar rsvd; /* Reserved */ - unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ - unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ - unchar icmbs; /* Number of incoming MBs, "" "" */ -} InitCmd; - -/* - * Interrupt Status Port - also returns diagnostic codes at ASC reset - * - * if msb is zero, the lower bits are diagnostic status - * Diagnostics: - * 01 No diagnostic error occurred - * 02 RAM failure - * 03 FIFO R/W failed - * 04 SBIC register read/write failed - * 05 Initialization D-FF failed - * 06 Host IRQ D-FF failed - * 07 ROM checksum error - * Interrupt status (bitwise): - * 10NNNNNN outgoing mailbox NNNNNN is free - * 11NNNNNN incoming mailbox NNNNNN needs service - */ -#define MB_INTR 0xC0 /* Mailbox Service possible/required */ -#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */ -#define MB_MASK 0x3f /* mask for mailbox number */ - -/* - * CONTROL port bits - */ -#define INT_EN 0x08 /* Interrupt Enable */ -#define DMA_EN 0x04 /* DMA Enable */ -#define SCSI_RES 0x02 /* SCSI Reset */ -#define ASC_RES 0x01 /* ASC Reset */ - -/* - * Driver data structures: - * - mb and scbs are required for interfacing with the host adapter. - * An SCB has extra fields not visible to the adapter; mb's - * _cannot_ do this, since the adapter assumes they are contiguous in - * memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact - * to access them. - * - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each; - * the additional bytes are used only by the driver. - * - For now, a pool of SCBs are kept in global storage by this driver, - * and are allocated and freed as needed. - * - * The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command, - * not when it has finished. Since the SCB must be around for completion, - * problems arise when SCBs correspond to OGMBs, which may be reallocated - * earlier (or delayed unnecessarily until a command completes). - * Mailboxes are used as transient data structures, simply for - * carrying SCB addresses to/from the 7000-FASST2. - * - * Note also since SCBs are not "permanently" associated with mailboxes, - * there is no need to keep a global list of scsi_cmnd pointers indexed - * by OGMB. Again, SCBs reference their scsi_cmnds directly, so mailbox - * indices need not be involved. - */ - -/* - * WD7000-specific scatter/gather element structure - */ -typedef struct sgb { - unchar len[3]; - unchar ptr[3]; /* Also SCSI-style - MSB first */ -} Sgb; - -typedef struct scb { /* Command Control Block 5.4.1 */ - unchar op; /* Command Control Block Operation Code */ - unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ - /* Outbound data transfer, length is checked */ - /* Inbound data transfer, length is checked */ - /* Logical Unit Number */ - unchar cdb[12]; /* SCSI Command Block */ - volatile unchar status; /* SCSI Return Status */ - volatile unchar vue; /* Vendor Unique Error Code */ - unchar maxlen[3]; /* Maximum Data Transfer Length */ - unchar dataptr[3]; /* SCSI Data Block Pointer */ - unchar linkptr[3]; /* Next Command Link Pointer */ - unchar direc; /* Transfer Direction */ - unchar reserved2[6]; /* SCSI Command Descriptor Block */ - /* end of hardware SCB */ - struct scsi_cmnd *SCpnt;/* scsi_cmnd using this SCB */ - Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */ - Adapter *host; /* host adapter */ - struct scb *next; /* for lists of scbs */ -} Scb; - -/* - * This driver is written to allow host-only commands to be executed. - * These use a 16-byte block called an ICB. The format is extended by the - * driver to 18 bytes, to support the status returned in the ICMB and - * an execution phase code. - * - * There are other formats besides these; these are the ones I've tried - * to use. Formats for some of the defined ICB opcodes are not defined - * (notably, get/set unsolicited interrupt status) in my copy of the OEM - * manual, and others are ambiguous/hard to follow. - */ -#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */ -#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */ -#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */ -#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */ -#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */ -#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */ -#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */ - /* 0x87 is reserved */ -#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */ -#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */ -#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */ -#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */ -#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */ -#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */ -#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */ -#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */ - -typedef struct icbRecvCmd { - unchar op; - unchar IDlun; /* Initiator SCSI ID/lun */ - unchar len[3]; /* command buffer length */ - unchar ptr[3]; /* command buffer address */ - unchar rsvd[7]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbRecvCmd; - -typedef struct icbSendStat { - unchar op; - unchar IDlun; /* Target SCSI ID/lun */ - unchar stat; /* (outgoing) completion status byte 1 */ - unchar rsvd[12]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbSendStat; - -typedef struct icbRevLvl { - unchar op; - volatile unchar primary; /* primary revision level (returned) */ - volatile unchar secondary; /* secondary revision level (returned) */ - unchar rsvd[12]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbRevLvl; - -typedef struct icbUnsMask { /* I'm totally guessing here */ - unchar op; - volatile unchar mask[14]; /* mask bits */ -#if 0 - unchar rsvd[12]; /* reserved */ -#endif - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbUnsMask; - -typedef struct icbDiag { - unchar op; - unchar type; /* diagnostics type code (0-3) */ - unchar len[3]; /* buffer length */ - unchar ptr[3]; /* buffer address */ - unchar rsvd[7]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbDiag; - -#define ICB_DIAG_POWERUP 0 /* Power-up diags only */ -#define ICB_DIAG_WALKING 1 /* walking 1's pattern */ -#define ICB_DIAG_DMA 2 /* DMA - system memory diags */ -#define ICB_DIAG_FULL 3 /* do both 1 & 2 */ - -typedef struct icbParms { - unchar op; - unchar rsvd1; /* reserved */ - unchar len[3]; /* parms buffer length */ - unchar ptr[3]; /* parms buffer address */ - unchar idx[2]; /* index (MSB-LSB) */ - unchar rsvd2[5]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbParms; - -typedef struct icbAny { - unchar op; - unchar data[14]; /* format-specific data */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbAny; - -typedef union icb { - unchar op; /* ICB opcode */ - IcbRecvCmd recv_cmd; /* format for receive command */ - IcbSendStat send_stat; /* format for send status */ - IcbRevLvl rev_lvl; /* format for get revision level */ - IcbDiag diag; /* format for execute diagnostics */ - IcbParms eparms; /* format for get/set exec parms */ - IcbAny icb; /* generic format */ - unchar data[18]; -} Icb; - -#ifdef MODULE -static char *wd7000; -module_param(wd7000, charp, 0); -#endif - -/* - * Driver SCB structure pool. - * - * The SCBs declared here are shared by all host adapters; hence, this - * structure is not part of the Adapter structure. - */ -static Scb scbs[MAX_SCBS]; -static Scb *scbfree; /* free list */ -static int freescbs = MAX_SCBS; /* free list counter */ -static spinlock_t scbpool_lock; /* guards the scb free list and count */ - -/* - * END of data/declarations - code follows. - */ -static void __init setup_error(char *mesg, int *ints) -{ - if (ints[0] == 3) - printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n", ints[1], ints[2], ints[3], mesg); - else if (ints[0] == 4) - printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], mesg); - else - printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], ints[5], mesg); -} - - -/* - * Note: You can now set these options from the kernel's "command line". - * The syntax is: - * - * wd7000=,,[,[,]] - * - * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values - * are 8000ns for BUS_ON and 1875ns for BUS_OFF. - * eg: - * wd7000=7,6,0x350 - * - * will configure the driver for a WD-7000 controller - * using IRQ 15 with a DMA channel 6, at IO base address 0x350. - */ -static int __init wd7000_setup(char *str) -{ - static short wd7000_card_num; /* .bss will zero this */ - short i; - int ints[6]; - - (void) get_options(str, ARRAY_SIZE(ints), ints); - - if (wd7000_card_num >= NUM_CONFIGS) { - printk(KERN_ERR "%s: Too many \"wd7000=\" configurations in " "command line!\n", __func__); - return 0; - } - - if ((ints[0] < 3) || (ints[0] > 5)) { - printk(KERN_ERR "%s: Error in command line! " "Usage: wd7000=,,IO>[," "[,]]\n", __func__); - } else { - for (i = 0; i < NUM_IRQS; i++) - if (ints[1] == wd7000_irq[i]) - break; - - if (i == NUM_IRQS) { - setup_error("invalid IRQ.", ints); - return 0; - } else - configs[wd7000_card_num].irq = ints[1]; - - for (i = 0; i < NUM_DMAS; i++) - if (ints[2] == wd7000_dma[i]) - break; - - if (i == NUM_DMAS) { - setup_error("invalid DMA channel.", ints); - return 0; - } else - configs[wd7000_card_num].dma = ints[2]; - - for (i = 0; i < NUM_IOPORTS; i++) - if (ints[3] == wd7000_iobase[i]) - break; - - if (i == NUM_IOPORTS) { - setup_error("invalid I/O base address.", ints); - return 0; - } else - configs[wd7000_card_num].iobase = ints[3]; - - if (ints[0] > 3) { - if ((ints[4] < 500) || (ints[4] > 31875)) { - setup_error("BUS_ON value is out of range (500" " to 31875 nanoseconds)!", ints); - configs[wd7000_card_num].bus_on = BUS_ON; - } else - configs[wd7000_card_num].bus_on = ints[4] / 125; - } else - configs[wd7000_card_num].bus_on = BUS_ON; - - if (ints[0] > 4) { - if ((ints[5] < 500) || (ints[5] > 31875)) { - setup_error("BUS_OFF value is out of range (500" " to 31875 nanoseconds)!", ints); - configs[wd7000_card_num].bus_off = BUS_OFF; - } else - configs[wd7000_card_num].bus_off = ints[5] / 125; - } else - configs[wd7000_card_num].bus_off = BUS_OFF; - - if (wd7000_card_num) { - for (i = 0; i < (wd7000_card_num - 1); i++) { - int j = i + 1; - - for (; j < wd7000_card_num; j++) - if (configs[i].irq == configs[j].irq) { - setup_error("duplicated IRQ!", ints); - return 0; - } - if (configs[i].dma == configs[j].dma) { - setup_error("duplicated DMA " "channel!", ints); - return 0; - } - if (configs[i].iobase == configs[j].iobase) { - setup_error("duplicated I/O " "base address!", ints); - return 0; - } - } - } - - dprintk(KERN_DEBUG "wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, " - "BUS_ON=%dns, BUS_OFF=%dns\n", configs[wd7000_card_num].irq, configs[wd7000_card_num].dma, configs[wd7000_card_num].iobase, configs[wd7000_card_num].bus_on * 125, configs[wd7000_card_num].bus_off * 125); - - wd7000_card_num++; - } - return 1; -} - -__setup("wd7000=", wd7000_setup); - -static inline void any2scsi(unchar * scsi, int any) -{ - *scsi++ = (unsigned)any >> 16; - *scsi++ = (unsigned)any >> 8; - *scsi++ = any; -} - -static inline int scsi2int(unchar * scsi) -{ - return (scsi[0] << 16) | (scsi[1] << 8) | scsi[2]; -} - -static inline void wd7000_enable_intr(Adapter * host) -{ - host->control |= INT_EN; - outb(host->control, host->iobase + ASC_CONTROL); -} - - -static inline void wd7000_enable_dma(Adapter * host) -{ - unsigned long flags; - host->control |= DMA_EN; - outb(host->control, host->iobase + ASC_CONTROL); - - flags = claim_dma_lock(); - set_dma_mode(host->dma, DMA_MODE_CASCADE); - enable_dma(host->dma); - release_dma_lock(flags); - -} - - -#define WAITnexttimeout 200 /* 2 seconds */ - -static inline short WAIT(unsigned port, unsigned mask, unsigned allof, unsigned noneof) -{ - unsigned WAITbits; - unsigned long WAITtimeout = jiffies + WAITnexttimeout; - - while (time_before_eq(jiffies, WAITtimeout)) { - WAITbits = inb(port) & mask; - - if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0)) - return (0); - } - - return (1); -} - - -static inline int command_out(Adapter * host, unchar * cmd, int len) -{ - if (!WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - while (len--) { - do { - outb(*cmd, host->iobase + ASC_COMMAND); - WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0); - } while (inb(host->iobase + ASC_STAT) & CMD_REJ); - - cmd++; - } - - return (1); - } - - printk(KERN_WARNING "wd7000 command_out: WAIT failed(%d)\n", len + 1); - - return (0); -} - - -/* - * This version of alloc_scbs is in preparation for supporting multiple - * commands per lun and command chaining, by queueing pending commands. - * We will need to allocate Scbs in blocks since they will wait to be - * executed so there is the possibility of deadlock otherwise. - * Also, to keep larger requests from being starved by smaller requests, - * we limit access to this routine with an internal busy flag, so that - * the satisfiability of a request is not dependent on the size of the - * request. - */ -static inline Scb *alloc_scbs(struct Scsi_Host *host, int needed) -{ - Scb *scb, *p = NULL; - unsigned long flags; - unsigned long timeout = jiffies + WAITnexttimeout; - unsigned long now; - int i; - - if (needed <= 0) - return (NULL); /* sanity check */ - - spin_unlock_irq(host->host_lock); - - retry: - while (freescbs < needed) { - timeout = jiffies + WAITnexttimeout; - do { - /* FIXME: can we actually just yield here ?? */ - for (now = jiffies; now == jiffies;) - cpu_relax(); /* wait a jiffy */ - } while (freescbs < needed && time_before_eq(jiffies, timeout)); - /* - * If we get here with enough free Scbs, we can take them. - * Otherwise, we timed out and didn't get enough. - */ - if (freescbs < needed) { - printk(KERN_ERR "wd7000: can't get enough free SCBs.\n"); - return (NULL); - } - } - - /* Take the lock, then check we didn't get beaten, if so try again */ - spin_lock_irqsave(&scbpool_lock, flags); - if (freescbs < needed) { - spin_unlock_irqrestore(&scbpool_lock, flags); - goto retry; - } - - scb = scbfree; - freescbs -= needed; - for (i = 0; i < needed; i++) { - p = scbfree; - scbfree = p->next; - } - p->next = NULL; - - spin_unlock_irqrestore(&scbpool_lock, flags); - - spin_lock_irq(host->host_lock); - return (scb); -} - - -static inline void free_scb(Scb * scb) -{ - unsigned long flags; - - spin_lock_irqsave(&scbpool_lock, flags); - - memset(scb, 0, sizeof(Scb)); - scb->next = scbfree; - scbfree = scb; - freescbs++; - - spin_unlock_irqrestore(&scbpool_lock, flags); -} - - -static inline void init_scbs(void) -{ - int i; - - spin_lock_init(&scbpool_lock); - - /* This is only ever called before the SCB pool is active */ - - scbfree = &(scbs[0]); - memset(scbs, 0, sizeof(scbs)); - for (i = 0; i < MAX_SCBS - 1; i++) { - scbs[i].next = &(scbs[i + 1]); - scbs[i].SCpnt = NULL; - } - scbs[MAX_SCBS - 1].next = NULL; - scbs[MAX_SCBS - 1].SCpnt = NULL; -} - - -static int mail_out(Adapter * host, Scb * scbptr) -/* - * Note: this can also be used for ICBs; just cast to the parm type. - */ -{ - int i, ogmb; - unsigned long flags; - unchar start_ogmb; - Mailbox *ogmbs = host->mb.ogmb; - int *next_ogmb = &(host->next_ogmb); - - dprintk("wd7000_mail_out: 0x%06lx", (long) scbptr); - - /* We first look for a free outgoing mailbox */ - spin_lock_irqsave(host->sh->host_lock, flags); - ogmb = *next_ogmb; - for (i = 0; i < OGMB_CNT; i++) { - if (ogmbs[ogmb].status == 0) { - dprintk(" using OGMB 0x%x", ogmb); - ogmbs[ogmb].status = 1; - any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr); - - *next_ogmb = (ogmb + 1) % OGMB_CNT; - break; - } else - ogmb = (ogmb + 1) % OGMB_CNT; - } - spin_unlock_irqrestore(host->sh->host_lock, flags); - - dprintk(", scb is 0x%06lx", (long) scbptr); - - if (i >= OGMB_CNT) { - /* - * Alternatively, we might issue the "interrupt on free OGMB", - * and sleep, but it must be ensured that it isn't the init - * task running. Instead, this version assumes that the caller - * will be persistent, and try again. Since it's the adapter - * that marks OGMB's free, waiting even with interrupts off - * should work, since they are freed very quickly in most cases. - */ - dprintk(", no free OGMBs.\n"); - return (0); - } - - wd7000_enable_intr(host); - - start_ogmb = START_OGMB | ogmb; - command_out(host, &start_ogmb, 1); - - dprintk(", awaiting interrupt.\n"); - - return (1); -} - - -static int make_code(unsigned hosterr, unsigned scsierr) -{ -#ifdef WD7000_DEBUG - int in_error = hosterr; -#endif - - switch ((hosterr >> 8) & 0xff) { - case 0: /* Reserved */ - hosterr = DID_ERROR; - break; - case 1: /* Command Complete, no errors */ - hosterr = DID_OK; - break; - case 2: /* Command complete, error logged in scb status (scsierr) */ - hosterr = DID_OK; - break; - case 4: /* Command failed to complete - timeout */ - hosterr = DID_TIME_OUT; - break; - case 5: /* Command terminated; Bus reset by external device */ - hosterr = DID_RESET; - break; - case 6: /* Unexpected Command Received w/ host as target */ - hosterr = DID_BAD_TARGET; - break; - case 80: /* Unexpected Reselection */ - case 81: /* Unexpected Selection */ - hosterr = DID_BAD_INTR; - break; - case 82: /* Abort Command Message */ - hosterr = DID_ABORT; - break; - case 83: /* SCSI Bus Software Reset */ - case 84: /* SCSI Bus Hardware Reset */ - hosterr = DID_RESET; - break; - default: /* Reserved */ - hosterr = DID_ERROR; - } -#ifdef WD7000_DEBUG - if (scsierr || hosterr) - dprintk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", scsierr, in_error, hosterr); -#endif - return (scsierr | (hosterr << 16)); -} - -#define wd7000_intr_ack(host) outb (0, host->iobase + ASC_INTR_ACK) - - -static irqreturn_t wd7000_intr(int irq, void *dev_id) -{ - Adapter *host = (Adapter *) dev_id; - int flag, icmb, errstatus, icmb_status; - int host_error, scsi_error; - Scb *scb; /* for SCSI commands */ - IcbAny *icb; /* for host commands */ - struct scsi_cmnd *SCpnt; - Mailbox *icmbs = host->mb.icmb; - unsigned long flags; - - spin_lock_irqsave(host->sh->host_lock, flags); - host->int_counter++; - - dprintk("wd7000_intr: irq = %d, host = 0x%06lx\n", irq, (long) host); - - flag = inb(host->iobase + ASC_INTR_STAT); - - dprintk("wd7000_intr: intr stat = 0x%02x\n", flag); - - if (!(inb(host->iobase + ASC_STAT) & INT_IM)) { - /* NB: these are _very_ possible if IRQ 15 is being used, since - * it's the "garbage collector" on the 2nd 8259 PIC. Specifically, - * any interrupt signal into the 8259 which can't be identified - * comes out as 7 from the 8259, which is 15 to the host. Thus, it - * is a good thing the WD7000 has an interrupt status port, so we - * can sort these out. Otherwise, electrical noise and other such - * problems would be indistinguishable from valid interrupts... - */ - dprintk("wd7000_intr: phantom interrupt...\n"); - goto ack; - } - - if (!(flag & MB_INTR)) - goto ack; - - /* The interrupt is for a mailbox */ - if (!(flag & IMB_INTR)) { - dprintk("wd7000_intr: free outgoing mailbox\n"); - /* - * If sleep_on() and the "interrupt on free OGMB" command are - * used in mail_out(), wake_up() should correspondingly be called - * here. For now, we don't need to do anything special. - */ - goto ack; - } - - /* The interrupt is for an incoming mailbox */ - icmb = flag & MB_MASK; - icmb_status = icmbs[icmb].status; - if (icmb_status & 0x80) { /* unsolicited - result in ICMB */ - dprintk("wd7000_intr: unsolicited interrupt 0x%02x\n", icmb_status); - goto ack; - } - - /* Aaaargh! (Zaga) */ - scb = isa_bus_to_virt(scsi2int((unchar *) icmbs[icmb].scbptr)); - icmbs[icmb].status = 0; - if (scb->op & ICB_OP_MASK) { /* an SCB is done */ - icb = (IcbAny *) scb; - icb->status = icmb_status; - icb->phase = 0; - goto ack; - } - - SCpnt = scb->SCpnt; - if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */ - host_error = scb->vue | (icmb_status << 8); - scsi_error = scb->status; - errstatus = make_code(host_error, scsi_error); - SCpnt->result = errstatus; - - free_scb(scb); - - SCpnt->scsi_done(SCpnt); - } - - ack: - dprintk("wd7000_intr: return from interrupt handler\n"); - wd7000_intr_ack(host); - - spin_unlock_irqrestore(host->sh->host_lock, flags); - return IRQ_HANDLED; -} - -static int wd7000_queuecommand_lck(struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)) -{ - Scb *scb; - Sgb *sgb; - unchar *cdb = (unchar *) SCpnt->cmnd; - unchar idlun; - short cdblen; - int nseg; - Adapter *host = (Adapter *) SCpnt->device->host->hostdata; - - cdblen = SCpnt->cmd_len; - idlun = ((SCpnt->device->id << 5) & 0xe0) | (SCpnt->device->lun & 7); - SCpnt->scsi_done = done; - SCpnt->SCp.phase = 1; - scb = alloc_scbs(SCpnt->device->host, 1); - scb->idlun = idlun; - memcpy(scb->cdb, cdb, cdblen); - scb->direc = 0x40; /* Disable direction check */ - - scb->SCpnt = SCpnt; /* so we can find stuff later */ - SCpnt->host_scribble = (unchar *) scb; - scb->host = host; - - nseg = scsi_sg_count(SCpnt); - if (nseg > 1) { - struct scatterlist *sg; - unsigned i; - - dprintk("Using scatter/gather with %d elements.\n", nseg); - - sgb = scb->sgb; - scb->op = 1; - any2scsi(scb->dataptr, (int) sgb); - any2scsi(scb->maxlen, nseg * sizeof(Sgb)); - - scsi_for_each_sg(SCpnt, sg, nseg, i) { - any2scsi(sgb[i].ptr, isa_page_to_bus(sg_page(sg)) + sg->offset); - any2scsi(sgb[i].len, sg->length); - } - } else { - scb->op = 0; - if (nseg) { - struct scatterlist *sg = scsi_sglist(SCpnt); - any2scsi(scb->dataptr, isa_page_to_bus(sg_page(sg)) + sg->offset); - } - any2scsi(scb->maxlen, scsi_bufflen(SCpnt)); - } - - /* FIXME: drop lock and yield here ? */ - - while (!mail_out(host, scb)) - cpu_relax(); /* keep trying */ - - return 0; -} - -static DEF_SCSI_QCMD(wd7000_queuecommand) - -static int wd7000_diagnostics(Adapter * host, int code) -{ - static IcbDiag icb = { ICB_OP_DIAGNOSTICS }; - static unchar buf[256]; - unsigned long timeout; - - icb.type = code; - any2scsi(icb.len, sizeof(buf)); - any2scsi(icb.ptr, (int) &buf); - icb.phase = 1; - /* - * This routine is only called at init, so there should be OGMBs - * available. I'm assuming so here. If this is going to - * fail, I can just let the timeout catch the failure. - */ - mail_out(host, (struct scb *) &icb); - timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */ - while (icb.phase && time_before(jiffies, timeout)) { - cpu_relax(); /* wait for completion */ - barrier(); - } - - if (icb.phase) { - printk("wd7000_diagnostics: timed out.\n"); - return (0); - } - if (make_code(icb.vue | (icb.status << 8), 0)) { - printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", icb.vue, icb.status); - return (0); - } - - return (1); -} - - -static int wd7000_adapter_reset(Adapter * host) -{ - InitCmd init_cmd = { - INITIALIZATION, - 7, - host->bus_on, - host->bus_off, - 0, - {0, 0, 0}, - OGMB_CNT, - ICMB_CNT - }; - int diag; - /* - * Reset the adapter - only. The SCSI bus was initialized at power-up, - * and we need to do this just so we control the mailboxes, etc. - */ - outb(ASC_RES, host->iobase + ASC_CONTROL); - udelay(40); /* reset pulse: this is 40us, only need 25us */ - outb(0, host->iobase + ASC_CONTROL); - host->control = 0; /* this must always shadow ASC_CONTROL */ - - if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - printk(KERN_ERR "wd7000_init: WAIT timed out.\n"); - return -1; /* -1 = not ok */ - } - - if ((diag = inb(host->iobase + ASC_INTR_STAT)) != 1) { - printk("wd7000_init: "); - - switch (diag) { - case 2: - printk(KERN_ERR "RAM failure.\n"); - break; - case 3: - printk(KERN_ERR "FIFO R/W failed\n"); - break; - case 4: - printk(KERN_ERR "SBIC register R/W failed\n"); - break; - case 5: - printk(KERN_ERR "Initialization D-FF failed.\n"); - break; - case 6: - printk(KERN_ERR "Host IRQ D-FF failed.\n"); - break; - case 7: - printk(KERN_ERR "ROM checksum error.\n"); - break; - default: - printk(KERN_ERR "diagnostic code 0x%02Xh received.\n", diag); - } - return -1; - } - /* Clear mailboxes */ - memset(&(host->mb), 0, sizeof(host->mb)); - - /* Execute init command */ - any2scsi((unchar *) & (init_cmd.mailboxes), (int) &(host->mb)); - if (!command_out(host, (unchar *) & init_cmd, sizeof(init_cmd))) { - printk(KERN_ERR "wd7000_adapter_reset: adapter initialization failed.\n"); - return -1; - } - - if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) { - printk("wd7000_adapter_reset: WAIT timed out.\n"); - return -1; - } - return 0; -} - -static int wd7000_init(Adapter * host) -{ - if (wd7000_adapter_reset(host) == -1) - return 0; - - - if (request_irq(host->irq, wd7000_intr, 0, "wd7000", host)) { - printk("wd7000_init: can't get IRQ %d.\n", host->irq); - return (0); - } - if (request_dma(host->dma, "wd7000")) { - printk("wd7000_init: can't get DMA channel %d.\n", host->dma); - free_irq(host->irq, host); - return (0); - } - wd7000_enable_dma(host); - wd7000_enable_intr(host); - - if (!wd7000_diagnostics(host, ICB_DIAG_FULL)) { - free_dma(host->dma); - free_irq(host->irq, NULL); - return (0); - } - - return (1); -} - - -static void wd7000_revision(Adapter * host) -{ - static IcbRevLvl icb = { ICB_OP_GET_REVISION }; - - icb.phase = 1; - /* - * Like diagnostics, this is only done at init time, in fact, from - * wd7000_detect, so there should be OGMBs available. If it fails, - * the only damage will be that the revision will show up as 0.0, - * which in turn means that scatter/gather will be disabled. - */ - mail_out(host, (struct scb *) &icb); - while (icb.phase) { - cpu_relax(); /* wait for completion */ - barrier(); - } - host->rev1 = icb.primary; - host->rev2 = icb.secondary; -} - - -static int wd7000_set_info(struct Scsi_Host *host, char *buffer, int length) -{ - dprintk("Buffer = <%.*s>, length = %d\n", length, buffer, length); - - /* - * Currently this is a no-op - */ - dprintk("Sorry, this function is currently out of order...\n"); - return (length); -} - - -static int wd7000_show_info(struct seq_file *m, struct Scsi_Host *host) -{ - Adapter *adapter = (Adapter *)host->hostdata; - unsigned long flags; -#ifdef WD7000_DEBUG - Mailbox *ogmbs, *icmbs; - short count; -#endif - - spin_lock_irqsave(host->host_lock, flags); - seq_printf(m, "Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", host->host_no, adapter->rev1, adapter->rev2); - seq_printf(m, " IO base: 0x%x\n", adapter->iobase); - seq_printf(m, " IRQ: %d\n", adapter->irq); - seq_printf(m, " DMA channel: %d\n", adapter->dma); - seq_printf(m, " Interrupts: %d\n", adapter->int_counter); - seq_printf(m, " BUS_ON time: %d nanoseconds\n", adapter->bus_on * 125); - seq_printf(m, " BUS_OFF time: %d nanoseconds\n", adapter->bus_off * 125); - -#ifdef WD7000_DEBUG - ogmbs = adapter->mb.ogmb; - icmbs = adapter->mb.icmb; - - seq_printf(m, "\nControl port value: 0x%x\n", adapter->control); - seq_puts(m, "Incoming mailbox:\n"); - seq_printf(m, " size: %d\n", ICMB_CNT); - seq_puts(m, " queued messages: "); - - for (i = count = 0; i < ICMB_CNT; i++) - if (icmbs[i].status) { - count++; - seq_printf(m, "0x%x ", i); - } - - seq_puts(m, count ? "\n" : "none\n"); - - seq_puts(m, "Outgoing mailbox:\n"); - seq_printf(m, " size: %d\n", OGMB_CNT); - seq_printf(m, " next message: 0x%x\n", adapter->next_ogmb); - seq_puts(m, " queued messages: "); - - for (i = count = 0; i < OGMB_CNT; i++) - if (ogmbs[i].status) { - count++; - seq_printf(m, "0x%x ", i); - } - - seq_puts(m, count ? "\n" : "none\n"); -#endif - - spin_unlock_irqrestore(host->host_lock, flags); - - return 0; -} - - -/* - * Returns the number of adapters this driver is supporting. - * - * The source for hosts.c says to wait to call scsi_register until 100% - * sure about an adapter. We need to do it a little sooner here; we - * need the storage set up by scsi_register before wd7000_init, and - * changing the location of an Adapter structure is more trouble than - * calling scsi_unregister. - * - */ - -static __init int wd7000_detect(struct scsi_host_template *tpnt) -{ - short present = 0, biosaddr_ptr, sig_ptr, i, pass; - short biosptr[NUM_CONFIGS]; - unsigned iobase; - Adapter *host = NULL; - struct Scsi_Host *sh; - int unit = 0; - - dprintk("wd7000_detect: started\n"); - -#ifdef MODULE - if (wd7000) - wd7000_setup(wd7000); -#endif - - for (i = 0; i < UNITS; wd7000_host[i++] = NULL); - for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1); - - tpnt->proc_name = "wd7000"; - tpnt->show_info = &wd7000_show_info; - tpnt->write_info = wd7000_set_info; - - /* - * Set up SCB free list, which is shared by all adapters - */ - init_scbs(); - - for (pass = 0; pass < NUM_CONFIGS; pass++) { - /* - * First, search for BIOS SIGNATURE... - */ - for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++) - for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) { - for (i = 0; i < pass; i++) - if (biosptr[i] == biosaddr_ptr) - break; - - if (i == pass) { - void __iomem *biosaddr = ioremap(wd7000_biosaddr[biosaddr_ptr] + signatures[sig_ptr].ofs, - signatures[sig_ptr].len); - short bios_match = 1; - - if (biosaddr) - bios_match = check_signature(biosaddr, signatures[sig_ptr].sig, signatures[sig_ptr].len); - - iounmap(biosaddr); - - if (bios_match) - goto bios_matched; - } - } - - bios_matched: - /* - * BIOS SIGNATURE has been found. - */ -#ifdef WD7000_DEBUG - dprintk("wd7000_detect: pass %d\n", pass + 1); - - if (biosaddr_ptr == NUM_ADDRS) - dprintk("WD-7000 SST BIOS not detected...\n"); - else - dprintk("WD-7000 SST BIOS detected at 0x%lx: checking...\n", wd7000_biosaddr[biosaddr_ptr]); -#endif - - if (configs[pass].irq < 0) - continue; - - if (unit == UNITS) - continue; - - iobase = configs[pass].iobase; - - dprintk("wd7000_detect: check IO 0x%x region...\n", iobase); - - if (request_region(iobase, 4, "wd7000")) { - - dprintk("wd7000_detect: ASC reset (IO 0x%x) ...", iobase); - /* - * ASC reset... - */ - outb(ASC_RES, iobase + ASC_CONTROL); - msleep(10); - outb(0, iobase + ASC_CONTROL); - - if (WAIT(iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - dprintk("failed!\n"); - goto err_release; - } else - dprintk("ok!\n"); - - if (inb(iobase + ASC_INTR_STAT) == 1) { - /* - * We register here, to get a pointer to the extra space, - * which we'll use as the Adapter structure (host) for - * this adapter. It is located just after the registered - * Scsi_Host structure (sh), and is located by the empty - * array hostdata. - */ - sh = scsi_register(tpnt, sizeof(Adapter)); - if (sh == NULL) - goto err_release; - - host = (Adapter *) sh->hostdata; - - dprintk("wd7000_detect: adapter allocated at 0x%x\n", (int) host); - memset(host, 0, sizeof(Adapter)); - - host->irq = configs[pass].irq; - host->dma = configs[pass].dma; - host->iobase = iobase; - host->int_counter = 0; - host->bus_on = configs[pass].bus_on; - host->bus_off = configs[pass].bus_off; - host->sh = wd7000_host[unit] = sh; - unit++; - - dprintk("wd7000_detect: Trying init WD-7000 card at IO " "0x%x, IRQ %d, DMA %d...\n", host->iobase, host->irq, host->dma); - - if (!wd7000_init(host)) /* Initialization failed */ - goto err_unregister; - - /* - * OK from here - we'll use this adapter/configuration. - */ - wd7000_revision(host); /* important for scatter/gather */ - - /* - * For boards before rev 6.0, scatter/gather isn't supported. - */ - if (host->rev1 < 6) - sh->sg_tablesize = 1; - - present++; /* count it */ - - if (biosaddr_ptr != NUM_ADDRS) - biosptr[pass] = biosaddr_ptr; - - printk(KERN_INFO "Western Digital WD-7000 (rev %d.%d) ", host->rev1, host->rev2); - printk("using IO 0x%x, IRQ %d, DMA %d.\n", host->iobase, host->irq, host->dma); - printk(" BUS_ON time: %dns, BUS_OFF time: %dns\n", host->bus_on * 125, host->bus_off * 125); - } - } else - dprintk("wd7000_detect: IO 0x%x region already allocated!\n", iobase); - - continue; - - err_unregister: - scsi_unregister(sh); - err_release: - release_region(iobase, 4); - - } - - if (!present) - printk("Failed initialization of WD-7000 SCSI card!\n"); - - return (present); -} - -static int wd7000_release(struct Scsi_Host *shost) -{ - if (shost->irq) - free_irq(shost->irq, NULL); - if (shost->io_port && shost->n_io_port) - release_region(shost->io_port, shost->n_io_port); - scsi_unregister(shost); - return 0; -} - -#if 0 -/* - * I have absolutely NO idea how to do an abort with the WD7000... - */ -static int wd7000_abort(Scsi_Cmnd * SCpnt) -{ - Adapter *host = (Adapter *) SCpnt->device->host->hostdata; - - if (inb(host->iobase + ASC_STAT) & INT_IM) { - printk("wd7000_abort: lost interrupt\n"); - wd7000_intr_handle(host->irq, NULL, NULL); - return FAILED; - } - return FAILED; -} -#endif - -/* - * Last resort. Reinitialize the board. - */ - -static int wd7000_host_reset(struct scsi_cmnd *SCpnt) -{ - Adapter *host = (Adapter *) SCpnt->device->host->hostdata; - - spin_lock_irq(SCpnt->device->host->host_lock); - - if (wd7000_adapter_reset(host) < 0) { - spin_unlock_irq(SCpnt->device->host->host_lock); - return FAILED; - } - - wd7000_enable_intr(host); - - spin_unlock_irq(SCpnt->device->host->host_lock); - return SUCCESS; -} - -/* - * This was borrowed directly from aha1542.c. (Zaga) - */ - -static int wd7000_biosparam(struct scsi_device *sdev, - struct block_device *bdev, sector_t capacity, int *ip) -{ - char b[BDEVNAME_SIZE]; - - dprintk("wd7000_biosparam: dev=%s, size=%llu, ", - bdevname(bdev, b), (u64)capacity); - (void)b; /* unused var warning? */ - - /* - * try default translation - */ - ip[0] = 64; - ip[1] = 32; - ip[2] = capacity >> 11; - - /* - * for disks >1GB do some guessing - */ - if (ip[2] >= 1024) { - int info[3]; - - /* - * try to figure out the geometry from the partition table - */ - if ((scsicam_bios_param(bdev, capacity, info) < 0) || !(((info[0] == 64) && (info[1] == 32)) || ((info[0] == 255) && (info[1] == 63)))) { - printk("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n" " using extended translation.\n"); - - ip[0] = 255; - ip[1] = 63; - ip[2] = (unsigned long) capacity / (255 * 63); - } else { - ip[0] = info[0]; - ip[1] = info[1]; - ip[2] = info[2]; - - if (info[0] == 255) - printk(KERN_INFO "%s: current partition table is " "using extended translation.\n", __func__); - } - } - - dprintk("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]); - dprintk("WARNING: check, if the bios geometry is correct.\n"); - - return (0); -} - -MODULE_AUTHOR("Thomas Wuensche, John Boyd, Miroslav Zagorac"); -MODULE_DESCRIPTION("Driver for the WD7000 series ISA controllers"); -MODULE_LICENSE("GPL"); - -static struct scsi_host_template driver_template = { - .proc_name = "wd7000", - .show_info = wd7000_show_info, - .write_info = wd7000_set_info, - .name = "Western Digital WD-7000", - .detect = wd7000_detect, - .release = wd7000_release, - .queuecommand = wd7000_queuecommand, - .eh_host_reset_handler = wd7000_host_reset, - .bios_param = wd7000_biosparam, - .can_queue = WD7000_Q, - .this_id = 7, - .sg_tablesize = WD7000_SG, - .unchecked_isa_dma = 1, - .use_clustering = ENABLE_CLUSTERING, -}; - -#include "scsi_module.c" From 2393b111ed8839e58e6590998483748b1efb35ff Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 19 Sep 2016 08:50:48 -0700 Subject: [PATCH 164/173] scsi: in2000: remove from tree The driver has not seen any maintainer activity or other work that wasn't tree wide conversion or clenaups in the entire history of the git tree. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinicke Signed-off-by: Martin K. Petersen --- Documentation/scsi/00-INDEX | 2 - Documentation/scsi/in2000.txt | 202 --- Documentation/scsi/scsi-parameters.txt | 3 - drivers/scsi/Kconfig | 12 - drivers/scsi/Makefile | 1 - drivers/scsi/in2000.c | 2302 ------------------------ drivers/scsi/in2000.h | 412 ----- 7 files changed, 2934 deletions(-) delete mode 100644 Documentation/scsi/in2000.txt delete mode 100644 drivers/scsi/in2000.c delete mode 100644 drivers/scsi/in2000.h diff --git a/Documentation/scsi/00-INDEX b/Documentation/scsi/00-INDEX index c4b978a72f78..bb4a76f823e1 100644 --- a/Documentation/scsi/00-INDEX +++ b/Documentation/scsi/00-INDEX @@ -64,8 +64,6 @@ hpsa.txt - HP Smart Array Controller SCSI driver. hptiop.txt - HIGHPOINT ROCKETRAID 3xxx RAID DRIVER -in2000.txt - - info on in2000 driver libsas.txt - Serial Attached SCSI management layer. link_power_management_policy.txt diff --git a/Documentation/scsi/in2000.txt b/Documentation/scsi/in2000.txt deleted file mode 100644 index c3e2a90475d2..000000000000 --- a/Documentation/scsi/in2000.txt +++ /dev/null @@ -1,202 +0,0 @@ - -UPDATE NEWS: version 1.33 - 26 Aug 98 - - Interrupt management in this driver has become, over - time, increasingly odd and difficult to explain - this - has been mostly due to my own mental inadequacies. In - recent kernels, it has failed to function at all when - compiled for SMP. I've fixed that problem, and after - taking a fresh look at interrupts in general, greatly - reduced the number of places where they're fiddled - with. Done some heavy testing and it looks very good. - The driver now makes use of the __initfunc() and - __initdata macros to save about 4k of kernel memory. - Once again, the same code works for both 2.0.xx and - 2.1.xx kernels. - -UPDATE NEWS: version 1.32 - 28 Mar 98 - - Removed the check for legal IN2000 hardware versions: - It appears that the driver works fine with serial - EPROMs (the 8-pin chip that defines hardware rev) as - old as 2.1, so we'll assume that all cards are OK. - -UPDATE NEWS: version 1.31 - 6 Jul 97 - - Fixed a bug that caused incorrect SCSI status bytes to be - returned from commands sent to LUNs greater than 0. This - means that CDROM changers work now! Fixed a bug in the - handling of command-line arguments when loaded as a module. - Also put all the header data in in2000.h where it belongs. - There are no longer any differences between this driver in - the 2.1.xx source tree and the 2.0.xx tree, as of 2.0.31 - and 2.1.45 (or is it .46?) - this makes things much easier - for me... - -UPDATE NEWS: version 1.30 - 14 Oct 96 - - Fixed a bug in the code that sets the transfer direction - bit (DESTID_DPD in the WD_DESTINATION_ID register). There - are quite a few SCSI commands that do a write-to-device; - now we deal with all of them correctly. Thanks to Joerg - Dorchain for catching this one. - -UPDATE NEWS: version 1.29 - 24 Sep 96 - - The memory-mapped hardware on the card is now accessed via - the 'readb()' and 'readl()' macros - required by the new - memory management scheme in the 2.1.x kernel series. - As suggested by Andries Brouwer, 'bios_param()' no longer - forces an artificial 1023 track limit on drives. Also - removed some kludge-code left over from struggles with - older (buggy) compilers. - -UPDATE NEWS: version 1.28 - 07 May 96 - - Tightened up the "interrupts enabled/disabled" discipline - in 'in2000_queuecommand()' and maybe 1 or 2 other places. - I _think_ it may have been a little too lax, causing an - occasional crash during full moon. A fully functional - /proc interface is now in place - if you want to play - with it, start by doing 'cat /proc/scsi/in2000/0'. You - can also use it to change a few run-time parameters on - the fly, but it's mostly for debugging. The curious - should take a good look at 'in2000_proc_info()' in the - in2000.c file to get an understanding of what it's all - about; I figure that people who are really into it will - want to add features suited to their own needs... - Also, sync is now DISABLED by default. - -UPDATE NEWS: version 1.27 - 10 Apr 96 - - Fixed a well-hidden bug in the adaptive-disconnect code - that would show up every now and then during extreme - heavy loads involving 2 or more simultaneously active - devices. Thanks to Joe Mack for keeping my nose to the - grindstone on this one. - -UPDATE NEWS: version 1.26 - 07 Mar 96 - - 1.25 had a nasty bug that bit people with swap partitions - and tape drives. Also, in my attempt to guess my way - through Intel assembly language, I made an error in the - inline code for IO writes. Made a few other changes and - repairs - this version (fingers crossed) should work well. - -UPDATE NEWS: version 1.25 - 05 Mar 96 - - Kernel 1.3.70 interrupt mods added; old kernels still OK. - Big help from Bill Earnest and David Willmore on speed - testing and optimizing: I think there's a real improvement - in this area. - New! User-friendly command-line interface for LILO and - module loading - the old method is gone, so you'll need - to read the comments for 'setup_strings' near the top - of in2000.c. For people with CDROM's or other devices - that have a tough time with sync negotiation, you can - now selectively disable sync on individual devices - - search for the 'nosync' keyword in the command-line - comments. Some of you disable the BIOS on the card, which - caused the auto-detect function to fail; there is now a - command-line option to force detection of a ROM-less card. - -UPDATE NEWS: version 1.24a - 24 Feb 96 - - There was a bug in the synchronous transfer code. Only - a few people downloaded before I caught it - could have - been worse. - -UPDATE NEWS: version 1.24 - 23 Feb 96 - - Lots of good changes. Advice from Bill Earnest resulted - in much better detection of cards, more efficient usage - of the fifo, and (hopefully) faster data transfers. The - jury is still out on speed - I hope it's improved some. - One nifty new feature is a cool way of doing disconnect/ - reselect. The driver defaults to what I'm calling - 'adaptive disconnect' - meaning that each command is - evaluated individually as to whether or not it should be - run with the option to disconnect/reselect (if the device - chooses), or as a "SCSI-bus-hog". When several devices - are operating simultaneously, disconnects are usually an - advantage. In a single device system, or if only 1 device - is being accessed, transfers usually go faster if disconnects - are not allowed. - - - -The default arguments (you get these when you don't give an 'in2000' -command-line argument, or you give a blank argument) will cause -the driver to do adaptive disconnect, synchronous transfers, and a -minimum of debug messages. If you want to fool with the options, -search for 'setup_strings' near the top of the in2000.c file and -check the 'hostdata->args' section in in2000.h - but be warned! Not -everything is working yet (some things will never work, probably). -I believe that disabling disconnects (DIS_NEVER) will allow you -to choose a LEVEL2 value higher than 'L2_BASIC', but I haven't -spent a lot of time testing this. You might try 'ENABLE_CLUSTERING' -to see what happens: my tests showed little difference either way. -There's also a define called 'DEFAULT_SX_PER'; this sets the data -transfer speed for the asynchronous mode. I've put it at 500 ns -despite the fact that the card could handle settings of 376 or -252, because higher speeds may be a problem with poor quality -cables or improper termination; 500 ns is a compromise. You can -choose your own default through the command-line with the -'period' keyword. - - ------------------------------------------------- -*********** DIP switch settings ************** ------------------------------------------------- - - sw1-1 sw1-2 BIOS address (hex) - ----------------------------------------- - off off C8000 - CBFF0 - on off D8000 - DBFF0 - off on D0000 - D3FF0 - on on BIOS disabled - - sw1-3 sw1-4 IO port address (hex) - ------------------------------------ - off off 220 - 22F - on off 200 - 20F - off on 110 - 11F - on on 100 - 10F - - sw1-5 sw1-6 sw1-7 Interrupt - ------------------------------ - off off off 15 - off on off 14 - off off on 11 - off on on 10 - on - - disabled - - sw1-8 function depends on BIOS version. In earlier versions this - controlled synchronous data transfer support for MSDOS: - off = disabled - on = enabled - In later ROMs (starting with 01.3 in April 1994) sw1-8 controls - the "greater than 2 disk drive" feature that first appeared in - MSDOS 5.0 (ignored by Linux): - off = 2 drives maximum - on = 7 drives maximum - - sw1-9 Floppy controller - -------------------------- - off disabled - on enabled - ------------------------------------------------- - - I should mention that Drew Eckhardt's 'Generic NCR5380' sources - were my main inspiration, with lots of reference to the IN2000 - driver currently distributed in the kernel source. I also owe - much to a driver written by Hamish Macdonald for Linux-m68k(!). - And to Eric Wright for being an ALPHA guinea pig. And to Bill - Earnest for 2 tons of great input and information. And to David - Willmore for extensive 'bonnie' testing. And to Joe Mack for - continual testing and feedback. - - - John Shifflett jshiffle@netcom.com - diff --git a/Documentation/scsi/scsi-parameters.txt b/Documentation/scsi/scsi-parameters.txt index 5a5c6088a6ec..2135ff4d1099 100644 --- a/Documentation/scsi/scsi-parameters.txt +++ b/Documentation/scsi/scsi-parameters.txt @@ -47,9 +47,6 @@ parameters may be changed at runtime by the command gvp11= [HW,SCSI] - in2000= [HW,SCSI] - See header of drivers/scsi/in2000.c. - ips= [HW,SCSI] Adaptec / IBM ServeRAID controller See header of drivers/scsi/ips.c. diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 15c6e9f2162d..543005b00546 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -500,18 +500,6 @@ config SCSI_ADVANSYS To compile this driver as a module, choose M here: the module will be called advansys. -config SCSI_IN2000 - tristate "Always IN2000 SCSI support" - depends on ISA && SCSI - help - This is support for an ISA bus SCSI host adapter. You'll find more - information in . If it doesn't work - out of the box, you may have to change the jumpers for IRQ or - address selection. - - To compile this driver as a module, choose M here: the - module will be called in2000. - config SCSI_ARCMSR tristate "ARECA (ARC11xx/12xx/13xx/16xx) SATA/SAS RAID Host Adapter" depends on PCI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index d870cc51d0bb..07bf799bf8a9 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -75,7 +75,6 @@ obj-$(CONFIG_SCSI_PM8001) += pm8001/ obj-$(CONFIG_SCSI_ISCI) += isci/ obj-$(CONFIG_SCSI_IPS) += ips.o obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o -obj-$(CONFIG_SCSI_IN2000) += in2000.o obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o obj-$(CONFIG_SCSI_GENERIC_NCR5380_MMIO) += g_NCR5380_mmio.o obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c deleted file mode 100644 index 3882d9f519c8..000000000000 --- a/drivers/scsi/in2000.c +++ /dev/null @@ -1,2302 +0,0 @@ -/* - * in2000.c - Linux device driver for the - * Always IN2000 ISA SCSI card. - * - * Copyright (c) 1996 John Shifflett, GeoLog Consulting - * john@geolog.com - * jshiffle@netcom.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. - * - * For the avoidance of doubt the "preferred form" of this code is one which - * is in an open non patent encumbered format. Where cryptographic key signing - * forms part of the process of creating an executable the information - * including keys needed to generate an equivalently functional executable - * are deemed to be part of the source code. - * - * Drew Eckhardt's excellent 'Generic NCR5380' sources provided - * much of the inspiration and some of the code for this driver. - * The Linux IN2000 driver distributed in the Linux kernels through - * version 1.2.13 was an extremely valuable reference on the arcane - * (and still mysterious) workings of the IN2000's fifo. It also - * is where I lifted in2000_biosparam(), the gist of the card - * detection scheme, and other bits of code. Many thanks to the - * talented and courageous people who wrote, contributed to, and - * maintained that driver (including Brad McLean, Shaun Savage, - * Bill Earnest, Larry Doolittle, Roger Sunshine, John Luckey, - * Matt Postiff, Peter Lu, zerucha@shell.portal.com, and Eric - * Youngdale). I should also mention the driver written by - * Hamish Macdonald for the (GASP!) Amiga A2091 card, included - * in the Linux-m68k distribution; it gave me a good initial - * understanding of the proper way to run a WD33c93 chip, and I - * ended up stealing lots of code from it. - * - * _This_ driver is (I feel) an improvement over the old one in - * several respects: - * - All problems relating to the data size of a SCSI request are - * gone (as far as I know). The old driver couldn't handle - * swapping to partitions because that involved 4k blocks, nor - * could it deal with the st.c tape driver unmodified, because - * that usually involved 4k - 32k blocks. The old driver never - * quite got away from a morbid dependence on 2k block sizes - - * which of course is the size of the card's fifo. - * - * - Target Disconnection/Reconnection is now supported. Any - * system with more than one device active on the SCSI bus - * will benefit from this. The driver defaults to what I'm - * calling 'adaptive disconnect' - meaning that each command - * is evaluated individually as to whether or not it should - * be run with the option to disconnect/reselect (if the - * device chooses), or as a "SCSI-bus-hog". - * - * - Synchronous data transfers are now supported. Because there - * are a few devices (and many improperly terminated systems) - * that choke when doing sync, the default is sync DISABLED - * for all devices. This faster protocol can (and should!) - * be enabled on selected devices via the command-line. - * - * - Runtime operating parameters can now be specified through - * either the LILO or the 'insmod' command line. For LILO do: - * "in2000=blah,blah,blah" - * and with insmod go like: - * "insmod /usr/src/linux/modules/in2000.o setup_strings=blah,blah" - * The defaults should be good for most people. See the comment - * for 'setup_strings' below for more details. - * - * - The old driver relied exclusively on what the Western Digital - * docs call "Combination Level 2 Commands", which are a great - * idea in that the CPU is relieved of a lot of interrupt - * overhead. However, by accepting a certain (user-settable) - * amount of additional interrupts, this driver achieves - * better control over the SCSI bus, and data transfers are - * almost as fast while being much easier to define, track, - * and debug. - * - * - You can force detection of a card whose BIOS has been disabled. - * - * - Multiple IN2000 cards might almost be supported. I've tried to - * keep it in mind, but have no way to test... - * - * - * TODO: - * tagged queuing. multiple cards. - * - * - * NOTE: - * When using this or any other SCSI driver as a module, you'll - * find that with the stock kernel, at most _two_ SCSI hard - * drives will be linked into the device list (ie, usable). - * If your IN2000 card has more than 2 disks on its bus, you - * might want to change the define of 'SD_EXTRA_DEVS' in the - * 'hosts.h' file from 2 to whatever is appropriate. It took - * me a while to track down this surprisingly obscure and - * undocumented little "feature". - * - * - * People with bug reports, wish-lists, complaints, comments, - * or improvements are asked to pah-leeez email me (John Shifflett) - * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get - * this thing into as good a shape as possible, and I'm positive - * there are lots of lurking bugs and "Stupid Places". - * - * Updated for Linux 2.5 by Alan Cox - * - Using new_eh handler - * - Hopefully got all the locking right again - * See "FIXME" notes for items that could do with more work - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "scsi.h" -#include - -#define IN2000_VERSION "1.33-2.5" -#define IN2000_DATE "2002/11/03" - -#include "in2000.h" - - -/* - * 'setup_strings' is a single string used to pass operating parameters and - * settings from the kernel/module command-line to the driver. 'setup_args[]' - * is an array of strings that define the compile-time default values for - * these settings. If Linux boots with a LILO or insmod command-line, those - * settings are combined with 'setup_args[]'. Note that LILO command-lines - * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix. - * The driver recognizes the following keywords (lower case required) and - * arguments: - * - * - ioport:addr -Where addr is IO address of a (usually ROM-less) card. - * - noreset -No optional args. Prevents SCSI bus reset at boot time. - * - nosync:x -x is a bitmask where the 1st 7 bits correspond with - * the 7 possible SCSI devices (bit 0 for device #0, etc). - * Set a bit to PREVENT sync negotiation on that device. - * The driver default is sync DISABLED on all devices. - * - period:ns -ns is the minimum # of nanoseconds in a SCSI data transfer - * period. Default is 500; acceptable values are 250 - 1000. - * - disconnect:x -x = 0 to never allow disconnects, 2 to always allow them. - * x = 1 does 'adaptive' disconnects, which is the default - * and generally the best choice. - * - debug:x -If 'DEBUGGING_ON' is defined, x is a bitmask that causes - * various types of debug output to printed - see the DB_xxx - * defines in in2000.h - * - proc:x -If 'PROC_INTERFACE' is defined, x is a bitmask that - * determines how the /proc interface works and what it - * does - see the PR_xxx defines in in2000.h - * - * Syntax Notes: - * - Numeric arguments can be decimal or the '0x' form of hex notation. There - * _must_ be a colon between a keyword and its numeric argument, with no - * spaces. - * - Keywords are separated by commas, no spaces, in the standard kernel - * command-line manner. - * - A keyword in the 'nth' comma-separated command-line member will overwrite - * the 'nth' element of setup_args[]. A blank command-line member (in - * other words, a comma with no preceding keyword) will _not_ overwrite - * the corresponding setup_args[] element. - * - * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'): - * - in2000=ioport:0x220,noreset - * - in2000=period:250,disconnect:2,nosync:0x03 - * - in2000=debug:0x1e - * - in2000=proc:3 - */ - -/* Normally, no defaults are specified... */ -static char *setup_args[] = { "", "", "", "", "", "", "", "", "" }; - -/* filled in by 'insmod' */ -static char *setup_strings; - -module_param(setup_strings, charp, 0); - -static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num) -{ - write1_io(reg_num, IO_WD_ADDR); - return read1_io(IO_WD_DATA); -} - - -#define READ_AUX_STAT() read1_io(IO_WD_ASR) - - -static inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value) -{ - write1_io(reg_num, IO_WD_ADDR); - write1_io(value, IO_WD_DATA); -} - - -static inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd) -{ -/* while (READ_AUX_STAT() & ASR_CIP) - printk("|");*/ - write1_io(WD_COMMAND, IO_WD_ADDR); - write1_io(cmd, IO_WD_DATA); -} - - -static uchar read_1_byte(struct IN2000_hostdata *hostdata) -{ - uchar asr, x = 0; - - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write_3393_cmd(hostdata, WD_CMD_TRANS_INFO | 0x80); - do { - asr = READ_AUX_STAT(); - if (asr & ASR_DBR) - x = read_3393(hostdata, WD_DATA); - } while (!(asr & ASR_INT)); - return x; -} - - -static void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value) -{ - write1_io(WD_TRANSFER_COUNT_MSB, IO_WD_ADDR); - write1_io((value >> 16), IO_WD_DATA); - write1_io((value >> 8), IO_WD_DATA); - write1_io(value, IO_WD_DATA); -} - - -static unsigned long read_3393_count(struct IN2000_hostdata *hostdata) -{ - unsigned long value; - - write1_io(WD_TRANSFER_COUNT_MSB, IO_WD_ADDR); - value = read1_io(IO_WD_DATA) << 16; - value |= read1_io(IO_WD_DATA) << 8; - value |= read1_io(IO_WD_DATA); - return value; -} - - -/* The 33c93 needs to be told which direction a command transfers its - * data; we use this function to figure it out. Returns true if there - * will be a DATA_OUT phase with this command, false otherwise. - * (Thanks to Joerg Dorchain for the research and suggestion.) - */ -static int is_dir_out(Scsi_Cmnd * cmd) -{ - switch (cmd->cmnd[0]) { - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_LONG: - case WRITE_SAME: - case WRITE_BUFFER: - case WRITE_VERIFY: - case WRITE_VERIFY_12: - case COMPARE: - case COPY: - case COPY_VERIFY: - case SEARCH_EQUAL: - case SEARCH_HIGH: - case SEARCH_LOW: - case SEARCH_EQUAL_12: - case SEARCH_HIGH_12: - case SEARCH_LOW_12: - case FORMAT_UNIT: - case REASSIGN_BLOCKS: - case RESERVE: - case MODE_SELECT: - case MODE_SELECT_10: - case LOG_SELECT: - case SEND_DIAGNOSTIC: - case CHANGE_DEFINITION: - case UPDATE_BLOCK: - case SET_WINDOW: - case MEDIUM_SCAN: - case SEND_VOLUME_TAG: - case 0xea: - return 1; - default: - return 0; - } -} - - - -static struct sx_period sx_table[] = { - {1, 0x20}, - {252, 0x20}, - {376, 0x30}, - {500, 0x40}, - {624, 0x50}, - {752, 0x60}, - {876, 0x70}, - {1000, 0x00}, - {0, 0} -}; - -static int round_period(unsigned int period) -{ - int x; - - for (x = 1; sx_table[x].period_ns; x++) { - if ((period <= sx_table[x - 0].period_ns) && (period > sx_table[x - 1].period_ns)) { - return x; - } - } - return 7; -} - -static uchar calc_sync_xfer(unsigned int period, unsigned int offset) -{ - uchar result; - - period *= 4; /* convert SDTR code to ns */ - result = sx_table[round_period(period)].reg_value; - result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF; - return result; -} - - - -static void in2000_execute(struct Scsi_Host *instance); - -static int in2000_queuecommand_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) -{ - struct Scsi_Host *instance; - struct IN2000_hostdata *hostdata; - Scsi_Cmnd *tmp; - - instance = cmd->device->host; - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - DB(DB_QUEUE_COMMAND, scmd_printk(KERN_DEBUG, cmd, "Q-%02x(", cmd->cmnd[0])) - -/* Set up a few fields in the Scsi_Cmnd structure for our own use: - * - host_scribble is the pointer to the next cmd in the input queue - * - scsi_done points to the routine we call when a cmd is finished - * - result is what you'd expect - */ - cmd->host_scribble = NULL; - cmd->scsi_done = done; - cmd->result = 0; - -/* We use the Scsi_Pointer structure that's included with each command - * as a scratchpad (as it's intended to be used!). The handy thing about - * the SCp.xxx fields is that they're always associated with a given - * cmd, and are preserved across disconnect-reselect. This means we - * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages - * if we keep all the critical pointers and counters in SCp: - * - SCp.ptr is the pointer into the RAM buffer - * - SCp.this_residual is the size of that buffer - * - SCp.buffer points to the current scatter-gather buffer - * - SCp.buffers_residual tells us how many S.G. buffers there are - * - SCp.have_data_in helps keep track of >2048 byte transfers - * - SCp.sent_command is not used - * - SCp.phase records this command's SRCID_ER bit setting - */ - - if (scsi_bufflen(cmd)) { - cmd->SCp.buffer = scsi_sglist(cmd); - cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1; - cmd->SCp.ptr = sg_virt(cmd->SCp.buffer); - cmd->SCp.this_residual = cmd->SCp.buffer->length; - } else { - cmd->SCp.buffer = NULL; - cmd->SCp.buffers_residual = 0; - cmd->SCp.ptr = NULL; - cmd->SCp.this_residual = 0; - } - cmd->SCp.have_data_in = 0; - -/* We don't set SCp.phase here - that's done in in2000_execute() */ - -/* WD docs state that at the conclusion of a "LEVEL2" command, the - * status byte can be retrieved from the LUN register. Apparently, - * this is the case only for *uninterrupted* LEVEL2 commands! If - * there are any unexpected phases entered, even if they are 100% - * legal (different devices may choose to do things differently), - * the LEVEL2 command sequence is exited. This often occurs prior - * to receiving the status byte, in which case the driver does a - * status phase interrupt and gets the status byte on its own. - * While such a command can then be "resumed" (ie restarted to - * finish up as a LEVEL2 command), the LUN register will NOT be - * a valid status byte at the command's conclusion, and we must - * use the byte obtained during the earlier interrupt. Here, we - * preset SCp.Status to an illegal value (0xff) so that when - * this command finally completes, we can tell where the actual - * status byte is stored. - */ - - cmd->SCp.Status = ILLEGAL_STATUS_BYTE; - -/* We need to disable interrupts before messing with the input - * queue and calling in2000_execute(). - */ - - /* - * Add the cmd to the end of 'input_Q'. Note that REQUEST_SENSE - * commands are added to the head of the queue so that the desired - * sense data is not lost before REQUEST_SENSE executes. - */ - - if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) { - cmd->host_scribble = (uchar *) hostdata->input_Q; - hostdata->input_Q = cmd; - } else { /* find the end of the queue */ - for (tmp = (Scsi_Cmnd *) hostdata->input_Q; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble); - tmp->host_scribble = (uchar *) cmd; - } - -/* We know that there's at least one command in 'input_Q' now. - * Go see if any of them are runnable! - */ - - in2000_execute(cmd->device->host); - - DB(DB_QUEUE_COMMAND, printk(")Q ")) - return 0; -} - -static DEF_SCSI_QCMD(in2000_queuecommand) - - - -/* - * This routine attempts to start a scsi command. If the host_card is - * already connected, we give up immediately. Otherwise, look through - * the input_Q, using the first command we find that's intended - * for a currently non-busy target/lun. - * Note that this function is always called with interrupts already - * disabled (either from in2000_queuecommand() or in2000_intr()). - */ -static void in2000_execute(struct Scsi_Host *instance) -{ - struct IN2000_hostdata *hostdata; - Scsi_Cmnd *cmd, *prev; - int i; - unsigned short *sp; - unsigned short f; - unsigned short flushbuf[16]; - - - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - DB(DB_EXECUTE, printk("EX(")) - - if (hostdata->selecting || hostdata->connected) { - - DB(DB_EXECUTE, printk(")EX-0 ")) - - return; - } - - /* - * Search through the input_Q for a command destined - * for an idle target/lun. - */ - - cmd = (Scsi_Cmnd *) hostdata->input_Q; - prev = NULL; - while (cmd) { - if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun))) - break; - prev = cmd; - cmd = (Scsi_Cmnd *) cmd->host_scribble; - } - - /* quit if queue empty or all possible targets are busy */ - - if (!cmd) { - - DB(DB_EXECUTE, printk(")EX-1 ")) - - return; - } - - /* remove command from queue */ - - if (prev) - prev->host_scribble = cmd->host_scribble; - else - hostdata->input_Q = (Scsi_Cmnd *) cmd->host_scribble; - -#ifdef PROC_STATISTICS - hostdata->cmd_cnt[cmd->device->id]++; -#endif - -/* - * Start the selection process - */ - - if (is_dir_out(cmd)) - write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id); - else - write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD); - -/* Now we need to figure out whether or not this command is a good - * candidate for disconnect/reselect. We guess to the best of our - * ability, based on a set of hierarchical rules. When several - * devices are operating simultaneously, disconnects are usually - * an advantage. In a single device system, or if only 1 device - * is being accessed, transfers usually go faster if disconnects - * are not allowed: - * - * + Commands should NEVER disconnect if hostdata->disconnect = - * DIS_NEVER (this holds for tape drives also), and ALWAYS - * disconnect if hostdata->disconnect = DIS_ALWAYS. - * + Tape drive commands should always be allowed to disconnect. - * + Disconnect should be allowed if disconnected_Q isn't empty. - * + Commands should NOT disconnect if input_Q is empty. - * + Disconnect should be allowed if there are commands in input_Q - * for a different target/lun. In this case, the other commands - * should be made disconnect-able, if not already. - * - * I know, I know - this code would flunk me out of any - * "C Programming 101" class ever offered. But it's easy - * to change around and experiment with for now. - */ - - cmd->SCp.phase = 0; /* assume no disconnect */ - if (hostdata->disconnect == DIS_NEVER) - goto no; - if (hostdata->disconnect == DIS_ALWAYS) - goto yes; - if (cmd->device->type == 1) /* tape drive? */ - goto yes; - if (hostdata->disconnected_Q) /* other commands disconnected? */ - goto yes; - if (!(hostdata->input_Q)) /* input_Q empty? */ - goto no; - for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble) { - if ((prev->device->id != cmd->device->id) || (prev->device->lun != cmd->device->lun)) { - for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble) - prev->SCp.phase = 1; - goto yes; - } - } - goto no; - - yes: - cmd->SCp.phase = 1; - -#ifdef PROC_STATISTICS - hostdata->disc_allowed_cnt[cmd->device->id]++; -#endif - - no: - write_3393(hostdata, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0)); - - write_3393(hostdata, WD_TARGET_LUN, cmd->device->lun); - write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]); - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); - - if ((hostdata->level2 <= L2_NONE) || (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) { - - /* - * Do a 'Select-With-ATN' command. This will end with - * one of the following interrupts: - * CSR_RESEL_AM: failure - can try again later. - * CSR_TIMEOUT: failure - give up. - * CSR_SELECT: success - proceed. - */ - - hostdata->selecting = cmd; - -/* Every target has its own synchronous transfer setting, kept in - * the sync_xfer array, and a corresponding status byte in sync_stat[]. - * Each target's sync_stat[] entry is initialized to SS_UNSET, and its - * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET - * means that the parameters are undetermined as yet, and that we - * need to send an SDTR message to this device after selection is - * complete. We set SS_FIRST to tell the interrupt routine to do so, - * unless we don't want to even _try_ synchronous transfers: In this - * case we set SS_SET to make the defaults final. - */ - if (hostdata->sync_stat[cmd->device->id] == SS_UNSET) { - if (hostdata->sync_off & (1 << cmd->device->id)) - hostdata->sync_stat[cmd->device->id] = SS_SET; - else - hostdata->sync_stat[cmd->device->id] = SS_FIRST; - } - hostdata->state = S_SELECTING; - write_3393_count(hostdata, 0); /* this guarantees a DATA_PHASE interrupt */ - write_3393_cmd(hostdata, WD_CMD_SEL_ATN); - } - - else { - - /* - * Do a 'Select-With-ATN-Xfer' command. This will end with - * one of the following interrupts: - * CSR_RESEL_AM: failure - can try again later. - * CSR_TIMEOUT: failure - give up. - * anything else: success - proceed. - */ - - hostdata->connected = cmd; - write_3393(hostdata, WD_COMMAND_PHASE, 0); - - /* copy command_descriptor_block into WD chip - * (take advantage of auto-incrementing) - */ - - write1_io(WD_CDB_1, IO_WD_ADDR); - for (i = 0; i < cmd->cmd_len; i++) - write1_io(cmd->cmnd[i], IO_WD_DATA); - - /* The wd33c93 only knows about Group 0, 1, and 5 commands when - * it's doing a 'select-and-transfer'. To be safe, we write the - * size of the CDB into the OWN_ID register for every case. This - * way there won't be problems with vendor-unique, audio, etc. - */ - - write_3393(hostdata, WD_OWN_ID, cmd->cmd_len); - - /* When doing a non-disconnect command, we can save ourselves a DATA - * phase interrupt later by setting everything up now. With writes we - * need to pre-fill the fifo; if there's room for the 32 flush bytes, - * put them in there too - that'll avoid a fifo interrupt. Reads are - * somewhat simpler. - * KLUDGE NOTE: It seems that you can't completely fill the fifo here: - * This results in the IO_FIFO_COUNT register rolling over to zero, - * and apparently the gate array logic sees this as empty, not full, - * so the 3393 chip is never signalled to start reading from the - * fifo. Or maybe it's seen as a permanent fifo interrupt condition. - * Regardless, we fix this by temporarily pretending that the fifo - * is 16 bytes smaller. (I see now that the old driver has a comment - * about "don't fill completely" in an analogous place - must be the - * same deal.) This results in CDROM, swap partitions, and tape drives - * needing an extra interrupt per write command - I think we can live - * with that! - */ - - if (!(cmd->SCp.phase)) { - write_3393_count(hostdata, cmd->SCp.this_residual); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS); - write1_io(0, IO_FIFO_WRITE); /* clear fifo counter, write mode */ - - if (is_dir_out(cmd)) { - hostdata->fifo = FI_FIFO_WRITING; - if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16)) - i = IN2000_FIFO_SIZE - 16; - cmd->SCp.have_data_in = i; /* this much data in fifo */ - i >>= 1; /* Gulp. Assuming modulo 2. */ - sp = (unsigned short *) cmd->SCp.ptr; - f = hostdata->io_base + IO_FIFO; - -#ifdef FAST_WRITE_IO - - FAST_WRITE2_IO(); -#else - while (i--) - write2_io(*sp++, IO_FIFO); - -#endif - - /* Is there room for the flush bytes? */ - - if (cmd->SCp.have_data_in <= ((IN2000_FIFO_SIZE - 16) - 32)) { - sp = flushbuf; - i = 16; - -#ifdef FAST_WRITE_IO - - FAST_WRITE2_IO(); -#else - while (i--) - write2_io(0, IO_FIFO); - -#endif - - } - } - - else { - write1_io(0, IO_FIFO_READ); /* put fifo in read mode */ - hostdata->fifo = FI_FIFO_READING; - cmd->SCp.have_data_in = 0; /* nothing transferred yet */ - } - - } else { - write_3393_count(hostdata, 0); /* this guarantees a DATA_PHASE interrupt */ - } - hostdata->state = S_RUNNING_LEVEL2; - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - } - - /* - * Since the SCSI bus can handle only 1 connection at a time, - * we get out of here now. If the selection fails, or when - * the command disconnects, we'll come back to this routine - * to search the input_Q again... - */ - - DB(DB_EXECUTE, printk("%s)EX-2 ", (cmd->SCp.phase) ? "d:" : "")) - -} - - - -static void transfer_pio(uchar * buf, int cnt, int data_in_dir, struct IN2000_hostdata *hostdata) -{ - uchar asr; - - DB(DB_TRANSFER, printk("(%p,%d,%s)", buf, cnt, data_in_dir ? "in" : "out")) - - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write_3393_count(hostdata, cnt); - write_3393_cmd(hostdata, WD_CMD_TRANS_INFO); - if (data_in_dir) { - do { - asr = READ_AUX_STAT(); - if (asr & ASR_DBR) - *buf++ = read_3393(hostdata, WD_DATA); - } while (!(asr & ASR_INT)); - } else { - do { - asr = READ_AUX_STAT(); - if (asr & ASR_DBR) - write_3393(hostdata, WD_DATA, *buf++); - } while (!(asr & ASR_INT)); - } - - /* Note: we are returning with the interrupt UN-cleared. - * Since (presumably) an entire I/O operation has - * completed, the bus phase is probably different, and - * the interrupt routine will discover this when it - * responds to the uncleared int. - */ - -} - - - -static void transfer_bytes(Scsi_Cmnd * cmd, int data_in_dir) -{ - struct IN2000_hostdata *hostdata; - unsigned short *sp; - unsigned short f; - int i; - - hostdata = (struct IN2000_hostdata *) cmd->device->host->hostdata; - -/* Normally, you'd expect 'this_residual' to be non-zero here. - * In a series of scatter-gather transfers, however, this - * routine will usually be called with 'this_residual' equal - * to 0 and 'buffers_residual' non-zero. This means that a - * previous transfer completed, clearing 'this_residual', and - * now we need to setup the next scatter-gather buffer as the - * source or destination for THIS transfer. - */ - if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { - ++cmd->SCp.buffer; - --cmd->SCp.buffers_residual; - cmd->SCp.this_residual = cmd->SCp.buffer->length; - cmd->SCp.ptr = sg_virt(cmd->SCp.buffer); - } - -/* Set up hardware registers */ - - write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]); - write_3393_count(hostdata, cmd->SCp.this_residual); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS); - write1_io(0, IO_FIFO_WRITE); /* zero counter, assume write */ - -/* Reading is easy. Just issue the command and return - we'll - * get an interrupt later when we have actual data to worry about. - */ - - if (data_in_dir) { - write1_io(0, IO_FIFO_READ); - if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { - write_3393(hostdata, WD_COMMAND_PHASE, 0x45); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - hostdata->state = S_RUNNING_LEVEL2; - } else - write_3393_cmd(hostdata, WD_CMD_TRANS_INFO); - hostdata->fifo = FI_FIFO_READING; - cmd->SCp.have_data_in = 0; - return; - } - -/* Writing is more involved - we'll start the WD chip and write as - * much data to the fifo as we can right now. Later interrupts will - * write any bytes that don't make it at this stage. - */ - - if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { - write_3393(hostdata, WD_COMMAND_PHASE, 0x45); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - hostdata->state = S_RUNNING_LEVEL2; - } else - write_3393_cmd(hostdata, WD_CMD_TRANS_INFO); - hostdata->fifo = FI_FIFO_WRITING; - sp = (unsigned short *) cmd->SCp.ptr; - - if ((i = cmd->SCp.this_residual) > IN2000_FIFO_SIZE) - i = IN2000_FIFO_SIZE; - cmd->SCp.have_data_in = i; - i >>= 1; /* Gulp. We assume this_residual is modulo 2 */ - f = hostdata->io_base + IO_FIFO; - -#ifdef FAST_WRITE_IO - - FAST_WRITE2_IO(); -#else - while (i--) - write2_io(*sp++, IO_FIFO); - -#endif - -} - - -/* We need to use spin_lock_irqsave() & spin_unlock_irqrestore() in this - * function in order to work in an SMP environment. (I'd be surprised - * if the driver is ever used by anyone on a real multi-CPU motherboard, - * but it _does_ need to be able to compile and run in an SMP kernel.) - */ - -static irqreturn_t in2000_intr(int irqnum, void *dev_id) -{ - struct Scsi_Host *instance = dev_id; - struct IN2000_hostdata *hostdata; - Scsi_Cmnd *patch, *cmd; - uchar asr, sr, phs, id, lun, *ucp, msg; - int i, j; - unsigned long length; - unsigned short *sp; - unsigned short f; - unsigned long flags; - - hostdata = (struct IN2000_hostdata *) instance->hostdata; - -/* Get the spin_lock and disable further ints, for SMP */ - - spin_lock_irqsave(instance->host_lock, flags); - -#ifdef PROC_STATISTICS - hostdata->int_cnt++; -#endif - -/* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the - * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined - * with a big logic array, so it's a little different than what you might - * expect). As far as I know, there's no reason that BOTH can't be active - * at the same time, but there's a problem: while we can read the 3393 - * to tell if _it_ wants an interrupt, I don't know of a way to ask the - * fifo the same question. The best we can do is check the 3393 and if - * it _isn't_ the source of the interrupt, then we can be pretty sure - * that the fifo is the culprit. - * UPDATE: I have it on good authority (Bill Earnest) that bit 0 of the - * IO_FIFO_COUNT register mirrors the fifo interrupt state. I - * assume that bit clear means interrupt active. As it turns - * out, the driver really doesn't need to check for this after - * all, so my remarks above about a 'problem' can safely be - * ignored. The way the logic is set up, there's no advantage - * (that I can see) to worrying about it. - * - * It seems that the fifo interrupt signal is negated when we extract - * bytes during read or write bytes during write. - * - fifo will interrupt when data is moving from it to the 3393, and - * there are 31 (or less?) bytes left to go. This is sort of short- - * sighted: what if you don't WANT to do more? In any case, our - * response is to push more into the fifo - either actual data or - * dummy bytes if need be. Note that we apparently have to write at - * least 32 additional bytes to the fifo after an interrupt in order - * to get it to release the ones it was holding on to - writing fewer - * than 32 will result in another fifo int. - * UPDATE: Again, info from Bill Earnest makes this more understandable: - * 32 bytes = two counts of the fifo counter register. He tells - * me that the fifo interrupt is a non-latching signal derived - * from a straightforward boolean interpretation of the 7 - * highest bits of the fifo counter and the fifo-read/fifo-write - * state. Who'd a thought? - */ - - write1_io(0, IO_LED_ON); - asr = READ_AUX_STAT(); - if (!(asr & ASR_INT)) { /* no WD33c93 interrupt? */ - -/* Ok. This is definitely a FIFO-only interrupt. - * - * If FI_FIFO_READING is set, there are up to 2048 bytes waiting to be read, - * maybe more to come from the SCSI bus. Read as many as we can out of the - * fifo and into memory at the location of SCp.ptr[SCp.have_data_in], and - * update have_data_in afterwards. - * - * If we have FI_FIFO_WRITING, the FIFO has almost run out of bytes to move - * into the WD3393 chip (I think the interrupt happens when there are 31 - * bytes left, but it may be fewer...). The 3393 is still waiting, so we - * shove some more into the fifo, which gets things moving again. If the - * original SCSI command specified more than 2048 bytes, there may still - * be some of that data left: fine - use it (from SCp.ptr[SCp.have_data_in]). - * Don't forget to update have_data_in. If we've already written out the - * entire buffer, feed 32 dummy bytes to the fifo - they're needed to - * push out the remaining real data. - * (Big thanks to Bill Earnest for getting me out of the mud in here.) - */ - - cmd = (Scsi_Cmnd *) hostdata->connected; /* assume we're connected */ - CHECK_NULL(cmd, "fifo_int") - - if (hostdata->fifo == FI_FIFO_READING) { - - DB(DB_FIFO, printk("{R:%02x} ", read1_io(IO_FIFO_COUNT))) - - sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - i = read1_io(IO_FIFO_COUNT) & 0xfe; - i <<= 2; /* # of words waiting in the fifo */ - f = hostdata->io_base + IO_FIFO; - -#ifdef FAST_READ_IO - - FAST_READ2_IO(); -#else - while (i--) - *sp++ = read2_io(IO_FIFO); - -#endif - - i = sp - (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - i <<= 1; - cmd->SCp.have_data_in += i; - } - - else if (hostdata->fifo == FI_FIFO_WRITING) { - - DB(DB_FIFO, printk("{W:%02x} ", read1_io(IO_FIFO_COUNT))) - -/* If all bytes have been written to the fifo, flush out the stragglers. - * Note that while writing 16 dummy words seems arbitrary, we don't - * have another choice that I can see. What we really want is to read - * the 3393 transfer count register (that would tell us how many bytes - * needed flushing), but the TRANSFER_INFO command hasn't completed - * yet (not enough bytes!) and that register won't be accessible. So, - * we use 16 words - a number obtained through trial and error. - * UPDATE: Bill says this is exactly what Always does, so there. - * More thanks due him for help in this section. - */ - if (cmd->SCp.this_residual == cmd->SCp.have_data_in) { - i = 16; - while (i--) /* write 32 dummy bytes */ - write2_io(0, IO_FIFO); - } - -/* If there are still bytes left in the SCSI buffer, write as many as we - * can out to the fifo. - */ - - else { - sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - i = cmd->SCp.this_residual - cmd->SCp.have_data_in; /* bytes yet to go */ - j = read1_io(IO_FIFO_COUNT) & 0xfe; - j <<= 2; /* how many words the fifo has room for */ - if ((j << 1) > i) - j = (i >> 1); - while (j--) - write2_io(*sp++, IO_FIFO); - - i = sp - (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - i <<= 1; - cmd->SCp.have_data_in += i; - } - } - - else { - printk("*** Spurious FIFO interrupt ***"); - } - - write1_io(0, IO_LED_OFF); - -/* release the SMP spin_lock and restore irq state */ - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; - } - -/* This interrupt was triggered by the WD33c93 chip. The fifo interrupt - * may also be asserted, but we don't bother to check it: we get more - * detailed info from FIFO_READING and FIFO_WRITING (see below). - */ - - cmd = (Scsi_Cmnd *) hostdata->connected; /* assume we're connected */ - sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear the interrupt */ - phs = read_3393(hostdata, WD_COMMAND_PHASE); - - if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) { - printk("\nNR:wd-intr-1\n"); - write1_io(0, IO_LED_OFF); - -/* release the SMP spin_lock and restore irq state */ - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; - } - - DB(DB_INTR, printk("{%02x:%02x-", asr, sr)) - -/* After starting a FIFO-based transfer, the next _WD3393_ interrupt is - * guaranteed to be in response to the completion of the transfer. - * If we were reading, there's probably data in the fifo that needs - * to be copied into RAM - do that here. Also, we have to update - * 'this_residual' and 'ptr' based on the contents of the - * TRANSFER_COUNT register, in case the device decided to do an - * intermediate disconnect (a device may do this if it has to - * do a seek, or just to be nice and let other devices have - * some bus time during long transfers). - * After doing whatever is necessary with the fifo, we go on and - * service the WD3393 interrupt normally. - */ - if (hostdata->fifo == FI_FIFO_READING) { - -/* buffer index = start-of-buffer + #-of-bytes-already-read */ - - sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - -/* bytes remaining in fifo = (total-wanted - #-not-got) - #-already-read */ - - i = (cmd->SCp.this_residual - read_3393_count(hostdata)) - cmd->SCp.have_data_in; - i >>= 1; /* Gulp. We assume this will always be modulo 2 */ - f = hostdata->io_base + IO_FIFO; - -#ifdef FAST_READ_IO - - FAST_READ2_IO(); -#else - while (i--) - *sp++ = read2_io(IO_FIFO); - -#endif - - hostdata->fifo = FI_FIFO_UNUSED; - length = cmd->SCp.this_residual; - cmd->SCp.this_residual = read_3393_count(hostdata); - cmd->SCp.ptr += (length - cmd->SCp.this_residual); - - DB(DB_TRANSFER, printk("(%p,%d)", cmd->SCp.ptr, cmd->SCp.this_residual)) - - } - - else if (hostdata->fifo == FI_FIFO_WRITING) { - hostdata->fifo = FI_FIFO_UNUSED; - length = cmd->SCp.this_residual; - cmd->SCp.this_residual = read_3393_count(hostdata); - cmd->SCp.ptr += (length - cmd->SCp.this_residual); - - DB(DB_TRANSFER, printk("(%p,%d)", cmd->SCp.ptr, cmd->SCp.this_residual)) - - } - -/* Respond to the specific WD3393 interrupt - there are quite a few! */ - - switch (sr) { - - case CSR_TIMEOUT: - DB(DB_INTR, printk("TIMEOUT")) - - if (hostdata->state == S_RUNNING_LEVEL2) - hostdata->connected = NULL; - else { - cmd = (Scsi_Cmnd *) hostdata->selecting; /* get a valid cmd */ - CHECK_NULL(cmd, "csr_timeout") - hostdata->selecting = NULL; - } - - cmd->result = DID_NO_CONNECT << 16; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->state = S_UNCONNECTED; - cmd->scsi_done(cmd); - -/* We are not connected to a target - check to see if there - * are commands waiting to be executed. - */ - - in2000_execute(instance); - break; - - -/* Note: this interrupt should not occur in a LEVEL2 command */ - - case CSR_SELECT: - DB(DB_INTR, printk("SELECT")) - hostdata->connected = cmd = (Scsi_Cmnd *) hostdata->selecting; - CHECK_NULL(cmd, "csr_select") - hostdata->selecting = NULL; - - /* construct an IDENTIFY message with correct disconnect bit */ - - hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->device->lun); - if (cmd->SCp.phase) - hostdata->outgoing_msg[0] |= 0x40; - - if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) { -#ifdef SYNC_DEBUG - printk(" sending SDTR "); -#endif - - hostdata->sync_stat[cmd->device->id] = SS_WAITING; - - /* tack on a 2nd message to ask about synchronous transfers */ - - hostdata->outgoing_msg[1] = EXTENDED_MESSAGE; - hostdata->outgoing_msg[2] = 3; - hostdata->outgoing_msg[3] = EXTENDED_SDTR; - hostdata->outgoing_msg[4] = OPTIMUM_SX_PER / 4; - hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF; - hostdata->outgoing_len = 6; - } else - hostdata->outgoing_len = 1; - - hostdata->state = S_CONNECTED; - break; - - - case CSR_XFER_DONE | PHS_DATA_IN: - case CSR_UNEXP | PHS_DATA_IN: - case CSR_SRV_REQ | PHS_DATA_IN: - DB(DB_INTR, printk("IN-%d.%d", cmd->SCp.this_residual, cmd->SCp.buffers_residual)) - transfer_bytes(cmd, DATA_IN_DIR); - if (hostdata->state != S_RUNNING_LEVEL2) - hostdata->state = S_CONNECTED; - break; - - - case CSR_XFER_DONE | PHS_DATA_OUT: - case CSR_UNEXP | PHS_DATA_OUT: - case CSR_SRV_REQ | PHS_DATA_OUT: - DB(DB_INTR, printk("OUT-%d.%d", cmd->SCp.this_residual, cmd->SCp.buffers_residual)) - transfer_bytes(cmd, DATA_OUT_DIR); - if (hostdata->state != S_RUNNING_LEVEL2) - hostdata->state = S_CONNECTED; - break; - - -/* Note: this interrupt should not occur in a LEVEL2 command */ - - case CSR_XFER_DONE | PHS_COMMAND: - case CSR_UNEXP | PHS_COMMAND: - case CSR_SRV_REQ | PHS_COMMAND: - DB(DB_INTR, printk("CMND-%02x", cmd->cmnd[0])) - transfer_pio(cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata); - hostdata->state = S_CONNECTED; - break; - - - case CSR_XFER_DONE | PHS_STATUS: - case CSR_UNEXP | PHS_STATUS: - case CSR_SRV_REQ | PHS_STATUS: - DB(DB_INTR, printk("STATUS=")) - - cmd->SCp.Status = read_1_byte(hostdata); - DB(DB_INTR, printk("%02x", cmd->SCp.Status)) - if (hostdata->level2 >= L2_BASIC) { - sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */ - hostdata->state = S_RUNNING_LEVEL2; - write_3393(hostdata, WD_COMMAND_PHASE, 0x50); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - } else { - hostdata->state = S_CONNECTED; - } - break; - - - case CSR_XFER_DONE | PHS_MESS_IN: - case CSR_UNEXP | PHS_MESS_IN: - case CSR_SRV_REQ | PHS_MESS_IN: - DB(DB_INTR, printk("MSG_IN=")) - - msg = read_1_byte(hostdata); - sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */ - - hostdata->incoming_msg[hostdata->incoming_ptr] = msg; - if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE) - msg = EXTENDED_MESSAGE; - else - hostdata->incoming_ptr = 0; - - cmd->SCp.Message = msg; - switch (msg) { - - case COMMAND_COMPLETE: - DB(DB_INTR, printk("CCMP")) - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_PRE_CMP_DISC; - break; - - case SAVE_POINTERS: - DB(DB_INTR, printk("SDP")) - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - - case RESTORE_POINTERS: - DB(DB_INTR, printk("RDP")) - if (hostdata->level2 >= L2_BASIC) { - write_3393(hostdata, WD_COMMAND_PHASE, 0x45); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - hostdata->state = S_RUNNING_LEVEL2; - } else { - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - } - break; - - case DISCONNECT: - DB(DB_INTR, printk("DIS")) - cmd->device->disconnect = 1; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_PRE_TMP_DISC; - break; - - case MESSAGE_REJECT: - DB(DB_INTR, printk("REJ")) -#ifdef SYNC_DEBUG - printk("-REJ-"); -#endif - if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) - hostdata->sync_stat[cmd->device->id] = SS_SET; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - - case EXTENDED_MESSAGE: - DB(DB_INTR, printk("EXT")) - - ucp = hostdata->incoming_msg; - -#ifdef SYNC_DEBUG - printk("%02x", ucp[hostdata->incoming_ptr]); -#endif - /* Is this the last byte of the extended message? */ - - if ((hostdata->incoming_ptr >= 2) && (hostdata->incoming_ptr == (ucp[1] + 1))) { - - switch (ucp[2]) { /* what's the EXTENDED code? */ - case EXTENDED_SDTR: - id = calc_sync_xfer(ucp[3], ucp[4]); - if (hostdata->sync_stat[cmd->device->id] != SS_WAITING) { - -/* A device has sent an unsolicited SDTR message; rather than go - * through the effort of decoding it and then figuring out what - * our reply should be, we're just gonna say that we have a - * synchronous fifo depth of 0. This will result in asynchronous - * transfers - not ideal but so much easier. - * Actually, this is OK because it assures us that if we don't - * specifically ask for sync transfers, we won't do any. - */ - - write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ - hostdata->outgoing_msg[0] = EXTENDED_MESSAGE; - hostdata->outgoing_msg[1] = 3; - hostdata->outgoing_msg[2] = EXTENDED_SDTR; - hostdata->outgoing_msg[3] = hostdata->default_sx_per / 4; - hostdata->outgoing_msg[4] = 0; - hostdata->outgoing_len = 5; - hostdata->sync_xfer[cmd->device->id] = calc_sync_xfer(hostdata->default_sx_per / 4, 0); - } else { - hostdata->sync_xfer[cmd->device->id] = id; - } -#ifdef SYNC_DEBUG - printk("sync_xfer=%02x", hostdata->sync_xfer[cmd->device->id]); -#endif - hostdata->sync_stat[cmd->device->id] = SS_SET; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - case EXTENDED_WDTR: - write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ - printk("sending WDTR "); - hostdata->outgoing_msg[0] = EXTENDED_MESSAGE; - hostdata->outgoing_msg[1] = 2; - hostdata->outgoing_msg[2] = EXTENDED_WDTR; - hostdata->outgoing_msg[3] = 0; /* 8 bit transfer width */ - hostdata->outgoing_len = 4; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - default: - write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ - printk("Rejecting Unknown Extended Message(%02x). ", ucp[2]); - hostdata->outgoing_msg[0] = MESSAGE_REJECT; - hostdata->outgoing_len = 1; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - } - hostdata->incoming_ptr = 0; - } - - /* We need to read more MESS_IN bytes for the extended message */ - - else { - hostdata->incoming_ptr++; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - } - break; - - default: - printk("Rejecting Unknown Message(%02x) ", msg); - write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ - hostdata->outgoing_msg[0] = MESSAGE_REJECT; - hostdata->outgoing_len = 1; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - } - break; - - -/* Note: this interrupt will occur only after a LEVEL2 command */ - - case CSR_SEL_XFER_DONE: - -/* Make sure that reselection is enabled at this point - it may - * have been turned off for the command that just completed. - */ - - write_3393(hostdata, WD_SOURCE_ID, SRCID_ER); - if (phs == 0x60) { - DB(DB_INTR, printk("SX-DONE")) - cmd->SCp.Message = COMMAND_COMPLETE; - lun = read_3393(hostdata, WD_TARGET_LUN); - DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun)) - hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->state = S_UNCONNECTED; - if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) - cmd->SCp.Status = lun; - if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); - else - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - cmd->scsi_done(cmd); - -/* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. - */ - - in2000_execute(instance); - } else { - printk("%02x:%02x:%02x: Unknown SEL_XFER_DONE phase!!---", asr, sr, phs); - } - break; - - -/* Note: this interrupt will occur only after a LEVEL2 command */ - - case CSR_SDP: - DB(DB_INTR, printk("SDP")) - hostdata->state = S_RUNNING_LEVEL2; - write_3393(hostdata, WD_COMMAND_PHASE, 0x41); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - break; - - - case CSR_XFER_DONE | PHS_MESS_OUT: - case CSR_UNEXP | PHS_MESS_OUT: - case CSR_SRV_REQ | PHS_MESS_OUT: - DB(DB_INTR, printk("MSG_OUT=")) - -/* To get here, we've probably requested MESSAGE_OUT and have - * already put the correct bytes in outgoing_msg[] and filled - * in outgoing_len. We simply send them out to the SCSI bus. - * Sometimes we get MESSAGE_OUT phase when we're not expecting - * it - like when our SDTR message is rejected by a target. Some - * targets send the REJECT before receiving all of the extended - * message, and then seem to go back to MESSAGE_OUT for a byte - * or two. Not sure why, or if I'm doing something wrong to - * cause this to happen. Regardless, it seems that sending - * NOP messages in these situations results in no harm and - * makes everyone happy. - */ - if (hostdata->outgoing_len == 0) { - hostdata->outgoing_len = 1; - hostdata->outgoing_msg[0] = NOP; - } - transfer_pio(hostdata->outgoing_msg, hostdata->outgoing_len, DATA_OUT_DIR, hostdata); - DB(DB_INTR, printk("%02x", hostdata->outgoing_msg[0])) - hostdata->outgoing_len = 0; - hostdata->state = S_CONNECTED; - break; - - - case CSR_UNEXP_DISC: - -/* I think I've seen this after a request-sense that was in response - * to an error condition, but not sure. We certainly need to do - * something when we get this interrupt - the question is 'what?'. - * Let's think positively, and assume some command has finished - * in a legal manner (like a command that provokes a request-sense), - * so we treat it as a normal command-complete-disconnect. - */ - - -/* Make sure that reselection is enabled at this point - it may - * have been turned off for the command that just completed. - */ - - write_3393(hostdata, WD_SOURCE_ID, SRCID_ER); - if (cmd == NULL) { - printk(" - Already disconnected! "); - hostdata->state = S_UNCONNECTED; - -/* release the SMP spin_lock and restore irq state */ - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; - } - DB(DB_INTR, printk("UNEXP_DISC")) - hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); - else - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - cmd->scsi_done(cmd); - -/* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. - */ - - in2000_execute(instance); - break; - - - case CSR_DISC: - -/* Make sure that reselection is enabled at this point - it may - * have been turned off for the command that just completed. - */ - - write_3393(hostdata, WD_SOURCE_ID, SRCID_ER); - DB(DB_INTR, printk("DISC")) - if (cmd == NULL) { - printk(" - Already disconnected! "); - hostdata->state = S_UNCONNECTED; - } - switch (hostdata->state) { - case S_PRE_CMP_DISC: - hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->state = S_UNCONNECTED; - DB(DB_INTR, printk(":%d", cmd->SCp.Status)) - if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); - else - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - cmd->scsi_done(cmd); - break; - case S_PRE_TMP_DISC: - case S_RUNNING_LEVEL2: - cmd->host_scribble = (uchar *) hostdata->disconnected_Q; - hostdata->disconnected_Q = cmd; - hostdata->connected = NULL; - hostdata->state = S_UNCONNECTED; - -#ifdef PROC_STATISTICS - hostdata->disc_done_cnt[cmd->device->id]++; -#endif - - break; - default: - printk("*** Unexpected DISCONNECT interrupt! ***"); - hostdata->state = S_UNCONNECTED; - } - -/* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. - */ - - in2000_execute(instance); - break; - - - case CSR_RESEL_AM: - DB(DB_INTR, printk("RESEL")) - - /* First we have to make sure this reselection didn't */ - /* happen during Arbitration/Selection of some other device. */ - /* If yes, put losing command back on top of input_Q. */ - if (hostdata->level2 <= L2_NONE) { - - if (hostdata->selecting) { - cmd = (Scsi_Cmnd *) hostdata->selecting; - hostdata->selecting = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - cmd->host_scribble = (uchar *) hostdata->input_Q; - hostdata->input_Q = cmd; - } - } - - else { - - if (cmd) { - if (phs == 0x00) { - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - cmd->host_scribble = (uchar *) hostdata->input_Q; - hostdata->input_Q = cmd; - } else { - printk("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---", asr, sr, phs); - while (1) - printk("\r"); - } - } - - } - - /* OK - find out which device reselected us. */ - - id = read_3393(hostdata, WD_SOURCE_ID); - id &= SRCID_MASK; - - /* and extract the lun from the ID message. (Note that we don't - * bother to check for a valid message here - I guess this is - * not the right way to go, but....) - */ - - lun = read_3393(hostdata, WD_DATA); - if (hostdata->level2 < L2_RESELECT) - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - lun &= 7; - - /* Now we look for the command that's reconnecting. */ - - cmd = (Scsi_Cmnd *) hostdata->disconnected_Q; - patch = NULL; - while (cmd) { - if (id == cmd->device->id && lun == cmd->device->lun) - break; - patch = cmd; - cmd = (Scsi_Cmnd *) cmd->host_scribble; - } - - /* Hmm. Couldn't find a valid command.... What to do? */ - - if (!cmd) { - printk("---TROUBLE: target %d.%d not in disconnect queue---", id, lun); - break; - } - - /* Ok, found the command - now start it up again. */ - - if (patch) - patch->host_scribble = cmd->host_scribble; - else - hostdata->disconnected_Q = (Scsi_Cmnd *) cmd->host_scribble; - hostdata->connected = cmd; - - /* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]' - * because these things are preserved over a disconnect. - * But we DO need to fix the DPD bit so it's correct for this command. - */ - - if (is_dir_out(cmd)) - write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id); - else - write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD); - if (hostdata->level2 >= L2_RESELECT) { - write_3393_count(hostdata, 0); /* we want a DATA_PHASE interrupt */ - write_3393(hostdata, WD_COMMAND_PHASE, 0x45); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - hostdata->state = S_RUNNING_LEVEL2; - } else - hostdata->state = S_CONNECTED; - - break; - - default: - printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs); - } - - write1_io(0, IO_LED_OFF); - - DB(DB_INTR, printk("} ")) - -/* release the SMP spin_lock and restore irq state */ - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; -} - - - -#define RESET_CARD 0 -#define RESET_CARD_AND_BUS 1 -#define B_FLAG 0x80 - -/* - * Caller must hold instance lock! - */ - -static int reset_hardware(struct Scsi_Host *instance, int type) -{ - struct IN2000_hostdata *hostdata; - int qt, x; - - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - write1_io(0, IO_LED_ON); - if (type == RESET_CARD_AND_BUS) { - write1_io(0, IO_CARD_RESET); - x = read1_io(IO_HARDWARE); - } - x = read_3393(hostdata, WD_SCSI_STATUS); /* clear any WD intrpt */ - write_3393(hostdata, WD_OWN_ID, instance->this_id | OWNID_EAF | OWNID_RAF | OWNID_FS_8); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, calc_sync_xfer(hostdata->default_sx_per / 4, DEFAULT_SX_OFF)); - - write1_io(0, IO_FIFO_WRITE); /* clear fifo counter */ - write1_io(0, IO_FIFO_READ); /* start fifo out in read mode */ - write_3393(hostdata, WD_COMMAND, WD_CMD_RESET); - /* FIXME: timeout ?? */ - while (!(READ_AUX_STAT() & ASR_INT)) - cpu_relax(); /* wait for RESET to complete */ - - x = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */ - - write_3393(hostdata, WD_QUEUE_TAG, 0xa5); /* any random number */ - qt = read_3393(hostdata, WD_QUEUE_TAG); - if (qt == 0xa5) { - x |= B_FLAG; - write_3393(hostdata, WD_QUEUE_TAG, 0); - } - write_3393(hostdata, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write1_io(0, IO_LED_OFF); - return x; -} - - - -static int in2000_bus_reset(Scsi_Cmnd * cmd) -{ - struct Scsi_Host *instance; - struct IN2000_hostdata *hostdata; - int x; - unsigned long flags; - - instance = cmd->device->host; - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - printk(KERN_WARNING "scsi%d: Reset. ", instance->host_no); - - spin_lock_irqsave(instance->host_lock, flags); - - /* do scsi-reset here */ - reset_hardware(instance, RESET_CARD_AND_BUS); - for (x = 0; x < 8; x++) { - hostdata->busy[x] = 0; - hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF); - hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */ - } - hostdata->input_Q = NULL; - hostdata->selecting = NULL; - hostdata->connected = NULL; - hostdata->disconnected_Q = NULL; - hostdata->state = S_UNCONNECTED; - hostdata->fifo = FI_FIFO_UNUSED; - hostdata->incoming_ptr = 0; - hostdata->outgoing_len = 0; - - cmd->result = DID_RESET << 16; - - spin_unlock_irqrestore(instance->host_lock, flags); - return SUCCESS; -} - -static int __in2000_abort(Scsi_Cmnd * cmd) -{ - struct Scsi_Host *instance; - struct IN2000_hostdata *hostdata; - Scsi_Cmnd *tmp, *prev; - uchar sr, asr; - unsigned long timeout; - - instance = cmd->device->host; - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - printk(KERN_DEBUG "scsi%d: Abort-", instance->host_no); - printk("(asr=%02x,count=%ld,resid=%d,buf_resid=%d,have_data=%d,FC=%02x)- ", READ_AUX_STAT(), read_3393_count(hostdata), cmd->SCp.this_residual, cmd->SCp.buffers_residual, cmd->SCp.have_data_in, read1_io(IO_FIFO_COUNT)); - -/* - * Case 1 : If the command hasn't been issued yet, we simply remove it - * from the inout_Q. - */ - - tmp = (Scsi_Cmnd *) hostdata->input_Q; - prev = NULL; - while (tmp) { - if (tmp == cmd) { - if (prev) - prev->host_scribble = cmd->host_scribble; - cmd->host_scribble = NULL; - cmd->result = DID_ABORT << 16; - printk(KERN_WARNING "scsi%d: Abort - removing command from input_Q. ", instance->host_no); - cmd->scsi_done(cmd); - return SUCCESS; - } - prev = tmp; - tmp = (Scsi_Cmnd *) tmp->host_scribble; - } - -/* - * Case 2 : If the command is connected, we're going to fail the abort - * and let the high level SCSI driver retry at a later time or - * issue a reset. - * - * Timeouts, and therefore aborted commands, will be highly unlikely - * and handling them cleanly in this situation would make the common - * case of noresets less efficient, and would pollute our code. So, - * we fail. - */ - - if (hostdata->connected == cmd) { - - printk(KERN_WARNING "scsi%d: Aborting connected command - ", instance->host_no); - - printk("sending wd33c93 ABORT command - "); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write_3393_cmd(hostdata, WD_CMD_ABORT); - -/* Now we have to attempt to flush out the FIFO... */ - - printk("flushing fifo - "); - timeout = 1000000; - do { - asr = READ_AUX_STAT(); - if (asr & ASR_DBR) - read_3393(hostdata, WD_DATA); - } while (!(asr & ASR_INT) && timeout-- > 0); - sr = read_3393(hostdata, WD_SCSI_STATUS); - printk("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ", asr, sr, read_3393_count(hostdata), timeout); - - /* - * Abort command processed. - * Still connected. - * We must disconnect. - */ - - printk("sending wd33c93 DISCONNECT command - "); - write_3393_cmd(hostdata, WD_CMD_DISCONNECT); - - timeout = 1000000; - asr = READ_AUX_STAT(); - while ((asr & ASR_CIP) && timeout-- > 0) - asr = READ_AUX_STAT(); - sr = read_3393(hostdata, WD_SCSI_STATUS); - printk("asr=%02x, sr=%02x.", asr, sr); - - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->connected = NULL; - hostdata->state = S_UNCONNECTED; - cmd->result = DID_ABORT << 16; - cmd->scsi_done(cmd); - - in2000_execute(instance); - - return SUCCESS; - } - -/* - * Case 3: If the command is currently disconnected from the bus, - * we're not going to expend much effort here: Let's just return - * an ABORT_SNOOZE and hope for the best... - */ - - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_Q; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble) - if (cmd == tmp) { - printk(KERN_DEBUG "scsi%d: unable to abort disconnected command.\n", instance->host_no); - return FAILED; - } - -/* - * Case 4 : If we reached this point, the command was not found in any of - * the queues. - * - * We probably reached this point because of an unlikely race condition - * between the command completing successfully and the abortion code, - * so we won't panic, but we will notify the user in case something really - * broke. - */ - - in2000_execute(instance); - - printk("scsi%d: warning : SCSI command probably completed successfully" " before abortion. ", instance->host_no); - return SUCCESS; -} - -static int in2000_abort(Scsi_Cmnd * cmd) -{ - int rc; - - spin_lock_irq(cmd->device->host->host_lock); - rc = __in2000_abort(cmd); - spin_unlock_irq(cmd->device->host->host_lock); - - return rc; -} - - -#define MAX_IN2000_HOSTS 3 -#define MAX_SETUP_ARGS ARRAY_SIZE(setup_args) -#define SETUP_BUFFER_SIZE 200 -static char setup_buffer[SETUP_BUFFER_SIZE]; -static char setup_used[MAX_SETUP_ARGS]; -static int done_setup = 0; - -static void __init in2000_setup(char *str, int *ints) -{ - int i; - char *p1, *p2; - - strlcpy(setup_buffer, str, SETUP_BUFFER_SIZE); - p1 = setup_buffer; - i = 0; - while (*p1 && (i < MAX_SETUP_ARGS)) { - p2 = strchr(p1, ','); - if (p2) { - *p2 = '\0'; - if (p1 != p2) - setup_args[i] = p1; - p1 = p2 + 1; - i++; - } else { - setup_args[i] = p1; - break; - } - } - for (i = 0; i < MAX_SETUP_ARGS; i++) - setup_used[i] = 0; - done_setup = 1; -} - - -/* check_setup_args() returns index if key found, 0 if not - */ - -static int __init check_setup_args(char *key, int *val, char *buf) -{ - int x; - char *cp; - - for (x = 0; x < MAX_SETUP_ARGS; x++) { - if (setup_used[x]) - continue; - if (!strncmp(setup_args[x], key, strlen(key))) - break; - } - if (x == MAX_SETUP_ARGS) - return 0; - setup_used[x] = 1; - cp = setup_args[x] + strlen(key); - *val = -1; - if (*cp != ':') - return ++x; - cp++; - if ((*cp >= '0') && (*cp <= '9')) { - *val = simple_strtoul(cp, NULL, 0); - } - return ++x; -} - - - -/* The "correct" (ie portable) way to access memory-mapped hardware - * such as the IN2000 EPROM and dip switch is through the use of - * special macros declared in 'asm/io.h'. We use readb() and readl() - * when reading from the card's BIOS area in in2000_detect(). - */ -static u32 bios_tab[] in2000__INITDATA = { - 0xc8000, - 0xd0000, - 0xd8000, - 0 -}; - -static unsigned short base_tab[] in2000__INITDATA = { - 0x220, - 0x200, - 0x110, - 0x100, -}; - -static int int_tab[] in2000__INITDATA = { - 15, - 14, - 11, - 10 -}; - -static int probe_bios(u32 addr, u32 *s1, uchar *switches) -{ - void __iomem *p = ioremap(addr, 0x34); - if (!p) - return 0; - *s1 = readl(p + 0x10); - if (*s1 == 0x41564f4e || readl(p + 0x30) == 0x61776c41) { - /* Read the switch image that's mapped into EPROM space */ - *switches = ~readb(p + 0x20); - iounmap(p); - return 1; - } - iounmap(p); - return 0; -} - -static int __init in2000_detect(struct scsi_host_template * tpnt) -{ - struct Scsi_Host *instance; - struct IN2000_hostdata *hostdata; - int detect_count; - int bios; - int x; - unsigned short base; - uchar switches; - uchar hrev; - unsigned long flags; - int val; - char buf[32]; - -/* Thanks to help from Bill Earnest, probing for IN2000 cards is a - * pretty straightforward and fool-proof operation. There are 3 - * possible locations for the IN2000 EPROM in memory space - if we - * find a BIOS signature, we can read the dip switch settings from - * the byte at BIOS+32 (shadowed in by logic on the card). From 2 - * of the switch bits we get the card's address in IO space. There's - * an image of the dip switch there, also, so we have a way to back- - * check that this really is an IN2000 card. Very nifty. Use the - * 'ioport:xx' command-line parameter if your BIOS EPROM is absent - * or disabled. - */ - - if (!done_setup && setup_strings) - in2000_setup(setup_strings, NULL); - - detect_count = 0; - for (bios = 0; bios_tab[bios]; bios++) { - u32 s1 = 0; - if (check_setup_args("ioport", &val, buf)) { - base = val; - switches = ~inb(base + IO_SWITCHES) & 0xff; - printk("Forcing IN2000 detection at IOport 0x%x ", base); - bios = 2; - } -/* - * There have been a couple of BIOS versions with different layouts - * for the obvious ID strings. We look for the 2 most common ones and - * hope that they cover all the cases... - */ - else if (probe_bios(bios_tab[bios], &s1, &switches)) { - printk("Found IN2000 BIOS at 0x%x ", (unsigned int) bios_tab[bios]); - -/* Find out where the IO space is */ - - x = switches & (SW_ADDR0 | SW_ADDR1); - base = base_tab[x]; - -/* Check for the IN2000 signature in IO space. */ - - x = ~inb(base + IO_SWITCHES) & 0xff; - if (x != switches) { - printk("Bad IO signature: %02x vs %02x.\n", x, switches); - continue; - } - } else - continue; - -/* OK. We have a base address for the IO ports - run a few safety checks */ - - if (!(switches & SW_BIT7)) { /* I _think_ all cards do this */ - printk("There is no IN-2000 SCSI card at IOport 0x%03x!\n", base); - continue; - } - -/* Let's assume any hardware version will work, although the driver - * has only been tested on 0x21, 0x22, 0x25, 0x26, and 0x27. We'll - * print out the rev number for reference later, but accept them all. - */ - - hrev = inb(base + IO_HARDWARE); - - /* Bit 2 tells us if interrupts are disabled */ - if (switches & SW_DISINT) { - printk("The IN-2000 SCSI card at IOport 0x%03x ", base); - printk("is not configured for interrupt operation!\n"); - printk("This driver requires an interrupt: cancelling detection.\n"); - continue; - } - -/* Ok. We accept that there's an IN2000 at ioaddr 'base'. Now - * initialize it. - */ - - tpnt->proc_name = "in2000"; - instance = scsi_register(tpnt, sizeof(struct IN2000_hostdata)); - if (instance == NULL) - continue; - detect_count++; - hostdata = (struct IN2000_hostdata *) instance->hostdata; - instance->io_port = hostdata->io_base = base; - hostdata->dip_switch = switches; - hostdata->hrev = hrev; - - write1_io(0, IO_FIFO_WRITE); /* clear fifo counter */ - write1_io(0, IO_FIFO_READ); /* start fifo out in read mode */ - write1_io(0, IO_INTR_MASK); /* allow all ints */ - x = int_tab[(switches & (SW_INT0 | SW_INT1)) >> SW_INT_SHIFT]; - if (request_irq(x, in2000_intr, 0, "in2000", instance)) { - printk("in2000_detect: Unable to allocate IRQ.\n"); - detect_count--; - continue; - } - instance->irq = x; - instance->n_io_port = 13; - request_region(base, 13, "in2000"); /* lock in this IO space for our use */ - - for (x = 0; x < 8; x++) { - hostdata->busy[x] = 0; - hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF); - hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */ -#ifdef PROC_STATISTICS - hostdata->cmd_cnt[x] = 0; - hostdata->disc_allowed_cnt[x] = 0; - hostdata->disc_done_cnt[x] = 0; -#endif - } - hostdata->input_Q = NULL; - hostdata->selecting = NULL; - hostdata->connected = NULL; - hostdata->disconnected_Q = NULL; - hostdata->state = S_UNCONNECTED; - hostdata->fifo = FI_FIFO_UNUSED; - hostdata->level2 = L2_BASIC; - hostdata->disconnect = DIS_ADAPTIVE; - hostdata->args = DEBUG_DEFAULTS; - hostdata->incoming_ptr = 0; - hostdata->outgoing_len = 0; - hostdata->default_sx_per = DEFAULT_SX_PER; - -/* Older BIOS's had a 'sync on/off' switch - use its setting */ - - if (s1 == 0x41564f4e && (switches & SW_SYNC_DOS5)) - hostdata->sync_off = 0x00; /* sync defaults to on */ - else - hostdata->sync_off = 0xff; /* sync defaults to off */ - -#ifdef PROC_INTERFACE - hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS | PR_CONNECTED | PR_INPUTQ | PR_DISCQ | PR_STOP; -#ifdef PROC_STATISTICS - hostdata->int_cnt = 0; -#endif -#endif - - if (check_setup_args("nosync", &val, buf)) - hostdata->sync_off = val; - - if (check_setup_args("period", &val, buf)) - hostdata->default_sx_per = sx_table[round_period((unsigned int) val)].period_ns; - - if (check_setup_args("disconnect", &val, buf)) { - if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) - hostdata->disconnect = val; - else - hostdata->disconnect = DIS_ADAPTIVE; - } - - if (check_setup_args("noreset", &val, buf)) - hostdata->args ^= A_NO_SCSI_RESET; - - if (check_setup_args("level2", &val, buf)) - hostdata->level2 = val; - - if (check_setup_args("debug", &val, buf)) - hostdata->args = (val & DB_MASK); - -#ifdef PROC_INTERFACE - if (check_setup_args("proc", &val, buf)) - hostdata->proc = val; -#endif - - - /* FIXME: not strictly needed I think but the called code expects - to be locked */ - spin_lock_irqsave(instance->host_lock, flags); - x = reset_hardware(instance, (hostdata->args & A_NO_SCSI_RESET) ? RESET_CARD : RESET_CARD_AND_BUS); - spin_unlock_irqrestore(instance->host_lock, flags); - - hostdata->microcode = read_3393(hostdata, WD_CDB_1); - if (x & 0x01) { - if (x & B_FLAG) - hostdata->chip = C_WD33C93B; - else - hostdata->chip = C_WD33C93A; - } else - hostdata->chip = C_WD33C93; - - printk("dip_switch=%02x irq=%d ioport=%02x floppy=%s sync/DOS5=%s ", (switches & 0x7f), instance->irq, hostdata->io_base, (switches & SW_FLOPPY) ? "Yes" : "No", (switches & SW_SYNC_DOS5) ? "Yes" : "No"); - printk("hardware_ver=%02x chip=%s microcode=%02x\n", hrev, (hostdata->chip == C_WD33C93) ? "WD33c93" : (hostdata->chip == C_WD33C93A) ? "WD33c93A" : (hostdata->chip == C_WD33C93B) ? "WD33c93B" : "unknown", hostdata->microcode); -#ifdef DEBUGGING_ON - printk("setup_args = "); - for (x = 0; x < MAX_SETUP_ARGS; x++) - printk("%s,", setup_args[x]); - printk("\n"); -#endif - if (hostdata->sync_off == 0xff) - printk("Sync-transfer DISABLED on all devices: ENABLE from command-line\n"); - printk("IN2000 driver version %s - %s\n", IN2000_VERSION, IN2000_DATE); - } - - return detect_count; -} - -static int in2000_release(struct Scsi_Host *shost) -{ - if (shost->irq) - free_irq(shost->irq, shost); - if (shost->io_port && shost->n_io_port) - release_region(shost->io_port, shost->n_io_port); - return 0; -} - -/* NOTE: I lifted this function straight out of the old driver, - * and have not tested it. Presumably it does what it's - * supposed to do... - */ - -static int in2000_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int *iinfo) -{ - int size; - - size = capacity; - iinfo[0] = 64; - iinfo[1] = 32; - iinfo[2] = size >> 11; - -/* This should approximate the large drive handling that the DOS ASPI manager - uses. Drives very near the boundaries may not be handled correctly (i.e. - near 2.0 Gb and 4.0 Gb) */ - - if (iinfo[2] > 1024) { - iinfo[0] = 64; - iinfo[1] = 63; - iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]); - } - if (iinfo[2] > 1024) { - iinfo[0] = 128; - iinfo[1] = 63; - iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]); - } - if (iinfo[2] > 1024) { - iinfo[0] = 255; - iinfo[1] = 63; - iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]); - } - return 0; -} - - -static int in2000_write_info(struct Scsi_Host *instance, char *buf, int len) -{ - -#ifdef PROC_INTERFACE - - char *bp; - struct IN2000_hostdata *hd; - int x, i; - - hd = (struct IN2000_hostdata *) instance->hostdata; - - buf[len] = '\0'; - bp = buf; - if (!strncmp(bp, "debug:", 6)) { - bp += 6; - hd->args = simple_strtoul(bp, NULL, 0) & DB_MASK; - } else if (!strncmp(bp, "disconnect:", 11)) { - bp += 11; - x = simple_strtoul(bp, NULL, 0); - if (x < DIS_NEVER || x > DIS_ALWAYS) - x = DIS_ADAPTIVE; - hd->disconnect = x; - } else if (!strncmp(bp, "period:", 7)) { - bp += 7; - x = simple_strtoul(bp, NULL, 0); - hd->default_sx_per = sx_table[round_period((unsigned int) x)].period_ns; - } else if (!strncmp(bp, "resync:", 7)) { - bp += 7; - x = simple_strtoul(bp, NULL, 0); - for (i = 0; i < 7; i++) - if (x & (1 << i)) - hd->sync_stat[i] = SS_UNSET; - } else if (!strncmp(bp, "proc:", 5)) { - bp += 5; - hd->proc = simple_strtoul(bp, NULL, 0); - } else if (!strncmp(bp, "level2:", 7)) { - bp += 7; - hd->level2 = simple_strtoul(bp, NULL, 0); - } -#endif - return len; -} - -static int in2000_show_info(struct seq_file *m, struct Scsi_Host *instance) -{ - -#ifdef PROC_INTERFACE - unsigned long flags; - struct IN2000_hostdata *hd; - Scsi_Cmnd *cmd; - int x; - - hd = (struct IN2000_hostdata *) instance->hostdata; - - spin_lock_irqsave(instance->host_lock, flags); - if (hd->proc & PR_VERSION) - seq_printf(m, "\nVersion %s - %s.", IN2000_VERSION, IN2000_DATE); - - if (hd->proc & PR_INFO) { - seq_printf(m, "\ndip_switch=%02x: irq=%d io=%02x floppy=%s sync/DOS5=%s", (hd->dip_switch & 0x7f), instance->irq, hd->io_base, (hd->dip_switch & 0x40) ? "Yes" : "No", (hd->dip_switch & 0x20) ? "Yes" : "No"); - seq_puts(m, "\nsync_xfer[] = "); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%02x", hd->sync_xfer[x]); - seq_puts(m, "\nsync_stat[] = "); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%02x", hd->sync_stat[x]); - } -#ifdef PROC_STATISTICS - if (hd->proc & PR_STATISTICS) { - seq_puts(m, "\ncommands issued: "); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%ld", hd->cmd_cnt[x]); - seq_puts(m, "\ndisconnects allowed:"); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%ld", hd->disc_allowed_cnt[x]); - seq_puts(m, "\ndisconnects done: "); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%ld", hd->disc_done_cnt[x]); - seq_printf(m, "\ninterrupts: \t%ld", hd->int_cnt); - } -#endif - if (hd->proc & PR_CONNECTED) { - seq_puts(m, "\nconnected: "); - if (hd->connected) { - cmd = (Scsi_Cmnd *) hd->connected; - seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); - } - } - if (hd->proc & PR_INPUTQ) { - seq_puts(m, "\ninput_Q: "); - cmd = (Scsi_Cmnd *) hd->input_Q; - while (cmd) { - seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); - cmd = (Scsi_Cmnd *) cmd->host_scribble; - } - } - if (hd->proc & PR_DISCQ) { - seq_puts(m, "\ndisconnected_Q:"); - cmd = (Scsi_Cmnd *) hd->disconnected_Q; - while (cmd) { - seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); - cmd = (Scsi_Cmnd *) cmd->host_scribble; - } - } - if (hd->proc & PR_TEST) { - ; /* insert your own custom function here */ - } - seq_putc(m, '\n'); - spin_unlock_irqrestore(instance->host_lock, flags); -#endif /* PROC_INTERFACE */ - return 0; -} - -MODULE_LICENSE("GPL"); - - -static struct scsi_host_template driver_template = { - .proc_name = "in2000", - .write_info = in2000_write_info, - .show_info = in2000_show_info, - .name = "Always IN2000", - .detect = in2000_detect, - .release = in2000_release, - .queuecommand = in2000_queuecommand, - .eh_abort_handler = in2000_abort, - .eh_bus_reset_handler = in2000_bus_reset, - .bios_param = in2000_biosparam, - .can_queue = IN2000_CAN_Q, - .this_id = IN2000_HOST_ID, - .sg_tablesize = IN2000_SG, - .cmd_per_lun = IN2000_CPL, - .use_clustering = DISABLE_CLUSTERING, -}; -#include "scsi_module.c" diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h deleted file mode 100644 index 5821e1fbce08..000000000000 --- a/drivers/scsi/in2000.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * in2000.h - Linux device driver definitions for the - * Always IN2000 ISA SCSI card. - * - * IMPORTANT: This file is for version 1.33 - 26/Aug/1998 - * - * Copyright (c) 1996 John Shifflett, GeoLog Consulting - * john@geolog.com - * jshiffle@netcom.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. - * - */ - -#ifndef IN2000_H -#define IN2000_H - -#include - -#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */ -#ifdef PROC_INTERFACE -#define PROC_STATISTICS /* add code for keeping various real time stats */ -#endif - -#define SYNC_DEBUG /* extra info on sync negotiation printed */ -#define DEBUGGING_ON /* enable command-line debugging bitmask */ -#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */ - -#ifdef __i386__ -#define FAST_READ_IO /* No problems with these on my machine */ -#define FAST_WRITE_IO -#endif - -#ifdef DEBUGGING_ON -#define DB(f,a) if (hostdata->args & (f)) a; -#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */ -#else -#define DB(f,a) -#define CHECK_NULL(p,s) -#endif - -#define uchar unsigned char - -#define read1_io(a) (inb(hostdata->io_base+(a))) -#define read2_io(a) (inw(hostdata->io_base+(a))) -#define write1_io(b,a) (outb((b),hostdata->io_base+(a))) -#define write2_io(w,a) (outw((w),hostdata->io_base+(a))) - -#ifdef __i386__ -/* These inline assembly defines are derived from a patch - * sent to me by Bill Earnest. He's done a lot of very - * valuable thinking, testing, and coding during his effort - * to squeeze more speed out of this driver. I really think - * that we are doing IO at close to the maximum now with - * the fifo. (And yes, insw uses 'edi' while outsw uses - * 'esi'. Thanks Bill!) - */ - -#define FAST_READ2_IO() \ -({ \ -int __dummy_1,__dummy_2; \ - __asm__ __volatile__ ("\n \ - cld \n \ - orl %%ecx, %%ecx \n \ - jz 1f \n \ - rep \n \ - insw (%%dx),%%es:(%%edi) \n \ -1: " \ - : "=D" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2) /* output */ \ - : "2" (f), "0" (sp), "1" (i) /* input */ \ - ); /* trashed */ \ -}) - -#define FAST_WRITE2_IO() \ -({ \ -int __dummy_1,__dummy_2; \ - __asm__ __volatile__ ("\n \ - cld \n \ - orl %%ecx, %%ecx \n \ - jz 1f \n \ - rep \n \ - outsw %%ds:(%%esi),(%%dx) \n \ -1: " \ - : "=S" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2)/* output */ \ - : "2" (f), "0" (sp), "1" (i) /* input */ \ - ); /* trashed */ \ -}) -#endif - -/* IN2000 io_port offsets */ -#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */ -#define ASR_INT 0x80 -#define ASR_LCI 0x40 -#define ASR_BSY 0x20 -#define ASR_CIP 0x10 -#define ASR_PE 0x02 -#define ASR_DBR 0x01 -#define IO_WD_ADDR 0x00 /* W - 3393 address reg */ -#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */ -#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */ -#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */ -#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */ -#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */ -#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */ -#define IO_FIFO_READ 0x07 /* W - start fifo read */ -#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */ -#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */ -#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */ -#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */ -#define SW_DISINT 0x04 /* bit 2 true if ints disabled */ -#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */ -#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */ -#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */ -#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */ -#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */ -#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */ -#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */ -#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */ -#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */ -#define IMASK_WD 0x01 /* WD33c93 interrupt mask */ -#define IMASK_FIFO 0x02 /* FIFO interrupt mask */ - -/* wd register names */ -#define WD_OWN_ID 0x00 -#define WD_CONTROL 0x01 -#define WD_TIMEOUT_PERIOD 0x02 -#define WD_CDB_1 0x03 -#define WD_CDB_2 0x04 -#define WD_CDB_3 0x05 -#define WD_CDB_4 0x06 -#define WD_CDB_5 0x07 -#define WD_CDB_6 0x08 -#define WD_CDB_7 0x09 -#define WD_CDB_8 0x0a -#define WD_CDB_9 0x0b -#define WD_CDB_10 0x0c -#define WD_CDB_11 0x0d -#define WD_CDB_12 0x0e -#define WD_TARGET_LUN 0x0f -#define WD_COMMAND_PHASE 0x10 -#define WD_SYNCHRONOUS_TRANSFER 0x11 -#define WD_TRANSFER_COUNT_MSB 0x12 -#define WD_TRANSFER_COUNT 0x13 -#define WD_TRANSFER_COUNT_LSB 0x14 -#define WD_DESTINATION_ID 0x15 -#define WD_SOURCE_ID 0x16 -#define WD_SCSI_STATUS 0x17 -#define WD_COMMAND 0x18 -#define WD_DATA 0x19 -#define WD_QUEUE_TAG 0x1a -#define WD_AUXILIARY_STATUS 0x1f - -/* WD commands */ -#define WD_CMD_RESET 0x00 -#define WD_CMD_ABORT 0x01 -#define WD_CMD_ASSERT_ATN 0x02 -#define WD_CMD_NEGATE_ACK 0x03 -#define WD_CMD_DISCONNECT 0x04 -#define WD_CMD_RESELECT 0x05 -#define WD_CMD_SEL_ATN 0x06 -#define WD_CMD_SEL 0x07 -#define WD_CMD_SEL_ATN_XFER 0x08 -#define WD_CMD_SEL_XFER 0x09 -#define WD_CMD_RESEL_RECEIVE 0x0a -#define WD_CMD_RESEL_SEND 0x0b -#define WD_CMD_WAIT_SEL_RECEIVE 0x0c -#define WD_CMD_TRANS_ADDR 0x18 -#define WD_CMD_TRANS_INFO 0x20 -#define WD_CMD_TRANSFER_PAD 0x21 -#define WD_CMD_SBT_MODE 0x80 - -/* SCSI Bus Phases */ -#define PHS_DATA_OUT 0x00 -#define PHS_DATA_IN 0x01 -#define PHS_COMMAND 0x02 -#define PHS_STATUS 0x03 -#define PHS_MESS_OUT 0x06 -#define PHS_MESS_IN 0x07 - -/* Command Status Register definitions */ - - /* reset state interrupts */ -#define CSR_RESET 0x00 -#define CSR_RESET_AF 0x01 - - /* successful completion interrupts */ -#define CSR_RESELECT 0x10 -#define CSR_SELECT 0x11 -#define CSR_SEL_XFER_DONE 0x16 -#define CSR_XFER_DONE 0x18 - - /* paused or aborted interrupts */ -#define CSR_MSGIN 0x20 -#define CSR_SDP 0x21 -#define CSR_SEL_ABORT 0x22 -#define CSR_RESEL_ABORT 0x25 -#define CSR_RESEL_ABORT_AM 0x27 -#define CSR_ABORT 0x28 - - /* terminated interrupts */ -#define CSR_INVALID 0x40 -#define CSR_UNEXP_DISC 0x41 -#define CSR_TIMEOUT 0x42 -#define CSR_PARITY 0x43 -#define CSR_PARITY_ATN 0x44 -#define CSR_BAD_STATUS 0x45 -#define CSR_UNEXP 0x48 - - /* service required interrupts */ -#define CSR_RESEL 0x80 -#define CSR_RESEL_AM 0x81 -#define CSR_DISC 0x85 -#define CSR_SRV_REQ 0x88 - - /* Own ID/CDB Size register */ -#define OWNID_EAF 0x08 -#define OWNID_EHP 0x10 -#define OWNID_RAF 0x20 -#define OWNID_FS_8 0x00 -#define OWNID_FS_12 0x40 -#define OWNID_FS_16 0x80 - - /* Control register */ -#define CTRL_HSP 0x01 -#define CTRL_HA 0x02 -#define CTRL_IDI 0x04 -#define CTRL_EDI 0x08 -#define CTRL_HHP 0x10 -#define CTRL_POLLED 0x00 -#define CTRL_BURST 0x20 -#define CTRL_BUS 0x40 -#define CTRL_DMA 0x80 - - /* Timeout Period register */ -#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */ - - /* Synchronous Transfer Register */ -#define STR_FSS 0x80 - - /* Destination ID register */ -#define DSTID_DPD 0x40 -#define DATA_OUT_DIR 0 -#define DATA_IN_DIR 1 -#define DSTID_SCC 0x80 - - /* Source ID register */ -#define SRCID_MASK 0x07 -#define SRCID_SIV 0x08 -#define SRCID_DSP 0x20 -#define SRCID_ES 0x40 -#define SRCID_ER 0x80 - - - -#define ILLEGAL_STATUS_BYTE 0xff - - -#define DEFAULT_SX_PER 500 /* (ns) fairly safe */ -#define DEFAULT_SX_OFF 0 /* aka async */ - -#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */ -#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */ - -struct sx_period { - unsigned int period_ns; - uchar reg_value; - }; - - -struct IN2000_hostdata { - struct Scsi_Host *next; - uchar chip; /* what kind of wd33c93 chip? */ - uchar microcode; /* microcode rev if 'B' */ - unsigned short io_base; /* IO port base */ - unsigned int dip_switch; /* dip switch settings */ - unsigned int hrev; /* hardware revision of card */ - volatile uchar busy[8]; /* index = target, bit = lun */ - volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */ - volatile Scsi_Cmnd *selecting; /* trying to select this command */ - volatile Scsi_Cmnd *connected; /* currently connected command */ - volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */ - uchar state; /* what we are currently doing */ - uchar fifo; /* what the FIFO is up to */ - uchar level2; /* extent to which Level-2 commands are used */ - uchar disconnect; /* disconnect/reselect policy */ - unsigned int args; /* set from command-line argument */ - uchar incoming_msg[8]; /* filled during message_in phase */ - int incoming_ptr; /* mainly used with EXTENDED messages */ - uchar outgoing_msg[8]; /* send this during next message_out */ - int outgoing_len; /* length of outgoing message */ - unsigned int default_sx_per; /* default transfer period for SCSI bus */ - uchar sync_xfer[8]; /* sync_xfer reg settings per target */ - uchar sync_stat[8]; /* status of sync negotiation per target */ - uchar sync_off; /* bit mask: don't use sync with these targets */ -#ifdef PROC_INTERFACE - uchar proc; /* bit mask: what's in proc output */ -#ifdef PROC_STATISTICS - unsigned long cmd_cnt[8]; /* # of commands issued per target */ - unsigned long int_cnt; /* # of interrupts serviced */ - unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */ - unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/ -#endif -#endif - }; - - -/* defines for hostdata->chip */ - -#define C_WD33C93 0 -#define C_WD33C93A 1 -#define C_WD33C93B 2 -#define C_UNKNOWN_CHIP 100 - -/* defines for hostdata->state */ - -#define S_UNCONNECTED 0 -#define S_SELECTING 1 -#define S_RUNNING_LEVEL2 2 -#define S_CONNECTED 3 -#define S_PRE_TMP_DISC 4 -#define S_PRE_CMP_DISC 5 - -/* defines for hostdata->fifo */ - -#define FI_FIFO_UNUSED 0 -#define FI_FIFO_READING 1 -#define FI_FIFO_WRITING 2 - -/* defines for hostdata->level2 */ -/* NOTE: only the first 3 are trustworthy at this point - - * having trouble when more than 1 device is reading/writing - * at the same time... - */ - -#define L2_NONE 0 /* no combination commands - we get lots of ints */ -#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */ -#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */ -#define L2_DATA 3 /* resume after DATA_IN/OUT ints */ -#define L2_MOST 4 /* resume after anything except a RESELECT int */ -#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */ -#define L2_ALL 6 /* always resume */ - -/* defines for hostdata->disconnect */ - -#define DIS_NEVER 0 -#define DIS_ADAPTIVE 1 -#define DIS_ALWAYS 2 - -/* defines for hostdata->args */ - -#define DB_TEST 1<<0 -#define DB_FIFO 1<<1 -#define DB_QUEUE_COMMAND 1<<2 -#define DB_EXECUTE 1<<3 -#define DB_INTR 1<<4 -#define DB_TRANSFER 1<<5 -#define DB_MASK 0x3f - -#define A_NO_SCSI_RESET 1<<15 - - -/* defines for hostdata->sync_xfer[] */ - -#define SS_UNSET 0 -#define SS_FIRST 1 -#define SS_WAITING 2 -#define SS_SET 3 - -/* defines for hostdata->proc */ - -#define PR_VERSION 1<<0 -#define PR_INFO 1<<1 -#define PR_STATISTICS 1<<2 -#define PR_CONNECTED 1<<3 -#define PR_INPUTQ 1<<4 -#define PR_DISCQ 1<<5 -#define PR_TEST 1<<6 -#define PR_STOP 1<<7 - - -# include -# include -# define in2000__INITFUNC(function) __initfunc(function) -# define in2000__INIT __init -# define in2000__INITDATA __initdata -# define CLISPIN_LOCK(host,flags) spin_lock_irqsave(host->host_lock, flags) -# define CLISPIN_UNLOCK(host,flags) spin_unlock_irqrestore(host->host_lock, \ - flags) - -static int in2000_detect(struct scsi_host_template *) in2000__INIT; -static int in2000_queuecommand(struct Scsi_Host *, struct scsi_cmnd *); -static int in2000_abort(Scsi_Cmnd *); -static void in2000_setup(char *, int *) in2000__INIT; -static int in2000_biosparam(struct scsi_device *, struct block_device *, - sector_t, int *); -static int in2000_bus_reset(Scsi_Cmnd *); - - -#define IN2000_CAN_Q 16 -#define IN2000_SG SG_ALL -#define IN2000_CPL 2 -#define IN2000_HOST_ID 7 - -#endif /* IN2000_H */ From 180a186d9839a5c2a2d2a2a572d5e47f41bf9309 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 19 Sep 2016 08:50:49 -0700 Subject: [PATCH 165/173] scsi: ultrastor: remove from tree The driver has not seen any maintainer activity or other work that wasn't tree wide conversion or clenaups in the entire history of the git tree. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinicke Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 17 - drivers/scsi/Makefile | 1 - drivers/scsi/ultrastor.c | 1210 -------------------------------------- drivers/scsi/ultrastor.h | 80 --- 4 files changed, 1308 deletions(-) delete mode 100644 drivers/scsi/ultrastor.c delete mode 100644 drivers/scsi/ultrastor.h diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 543005b00546..57cf77f84ca3 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1425,23 +1425,6 @@ config SCSI_U14_34F_MAX_TAGS by the driver for each probed SCSI device is reported at boot time. This is equivalent to the "u14-34f=mq:8" boot option. -config SCSI_ULTRASTOR - tristate "UltraStor SCSI support" - depends on X86 && ISA && SCSI && ISA_DMA_API - ---help--- - This is support for the UltraStor 14F, 24F and 34F SCSI-2 host - adapter family. This driver is explained in section 3.12 of the - SCSI-HOWTO, available from - . If it doesn't work out - of the box, you may have to change some settings in - . - - Note that there is also another driver for the same hardware: - "UltraStor 14F/34F support", above. - - To compile this driver as a module, choose M here: the - module will be called ultrastor. - config SCSI_NSP32 tristate "Workbit NinjaSCSI-32Bi/UDE support" depends on PCI && SCSI && !64BIT diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 07bf799bf8a9..2e7d9efd86af 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -63,7 +63,6 @@ obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o obj-$(CONFIG_SCSI_DPT_I2O) += dpt_i2o.o obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o obj-$(CONFIG_SCSI_ARCMSR) += arcmsr/ -obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o obj-$(CONFIG_SCSI_AHA152X) += aha152x.o obj-$(CONFIG_SCSI_AHA1542) += aha1542.o obj-$(CONFIG_SCSI_AHA1740) += aha1740.o diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c deleted file mode 100644 index 14e0c40a68c9..000000000000 --- a/drivers/scsi/ultrastor.c +++ /dev/null @@ -1,1210 +0,0 @@ -/* - * ultrastor.c Copyright (C) 1992 David B. Gentzel - * Low-level SCSI driver for UltraStor 14F, 24F, and 34F - * by David B. Gentzel, Whitfield Software Services, Carnegie, PA - * (gentzel@nova.enet.dec.com) - * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu) - * 24F and multiple command support by John F. Carr (jfc@athena.mit.edu) - * John's work modified by Caleb Epstein (cae@jpmorgan.com) and - * Eric Youngdale (ericy@cais.com). - * Thanks to UltraStor for providing the necessary documentation - * - * This is an old driver, for the 14F and 34F you should be using the - * u14-34f driver instead. - */ - -/* - * TODO: - * 1. Find out why scatter/gather is limited to 16 requests per command. - * This is fixed, at least on the 24F, as of version 1.12 - CAE. - * 2. Look at command linking (mscp.command_link and - * mscp.command_link_id). (Does not work with many disks, - * and no performance increase. ERY). - * 3. Allow multiple adapters. - */ - -/* - * NOTES: - * The UltraStor 14F, 24F, and 34F are a family of intelligent, high - * performance SCSI-2 host adapters. They all support command queueing - * and scatter/gather I/O. Some of them can also emulate the standard - * WD1003 interface for use with OS's which don't support SCSI. Here - * is the scoop on the various models: - * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation. - * 14N - ISA HA with floppy support. I think that this is a non-DMA - * HA. Nothing further known. - * 24F - EISA Bus Master HA with floppy support and WD1003 emulation. - * 34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation). - * - * The 14F, 24F, and 34F are supported by this driver. - * - * Places flagged with a triple question-mark are things which are either - * unfinished, questionable, or wrong. - */ - -/* Changes from version 1.11 alpha to 1.12 - * - * Increased the size of the scatter-gather list to 33 entries for - * the 24F adapter (it was 16). I don't have the specs for the 14F - * or the 34F, so they may support larger s-g lists as well. - * - * Caleb Epstein - */ - -/* Changes from version 1.9 to 1.11 - * - * Patches to bring this driver up to speed with the default kernel - * driver which supports only the 14F and 34F adapters. This version - * should compile cleanly into 0.99.13, 0.99.12 and probably 0.99.11. - * - * Fixes from Eric Youngdale to fix a few possible race conditions and - * several problems with bit testing operations (insufficient - * parentheses). - * - * Removed the ultrastor_abort() and ultrastor_reset() functions - * (enclosed them in #if 0 / #endif). These functions, at least on - * the 24F, cause the SCSI bus to do odd things and generally lead to - * kernel panics and machine hangs. This is like the Adaptec code. - * - * Use check/snarf_region for 14f, 34f to avoid I/O space address conflicts. - */ - -/* Changes from version 1.8 to version 1.9 - * - * 0.99.11 patches (cae@jpmorgan.com) */ - -/* Changes from version 1.7 to version 1.8 - * - * Better error reporting. - */ - -/* Changes from version 1.6 to version 1.7 - * - * Removed CSIR command code. - * - * Better race condition avoidance (xchgb function added). - * - * Set ICM and OGM status to zero at probe (24F) - * - * reset sends soft reset to UltraStor adapter - * - * reset adapter if adapter interrupts with an invalid MSCP address - * - * handle aborted command interrupt (24F) - * - */ - -/* Changes from version 1.5 to version 1.6: - * - * Read MSCP address from ICM _before_ clearing the interrupt flag. - * This fixes a race condition. - */ - -/* Changes from version 1.4 to version 1.5: - * - * Abort now calls done when multiple commands are enabled. - * - * Clear busy when aborted command finishes, not when abort is called. - * - * More debugging messages for aborts. - */ - -/* Changes from version 1.3 to version 1.4: - * - * Enable automatic request of sense data on error (requires newer version - * of scsi.c to be useful). - * - * Fix PORT_OVERRIDE for 14F. - * - * Fix abort and reset to work properly (config.aborted wasn't cleared - * after it was tested, so after a command abort no further commands would - * work). - * - * Boot time test to enable SCSI bus reset (defaults to not allowing reset). - * - * Fix test for OGM busy -- the busy bit is in different places on the 24F. - * - * Release ICM slot by clearing first byte on 24F. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */ -#include "scsi.h" -#include -#include "ultrastor.h" - -#define FALSE 0 -#define TRUE 1 - -#ifndef ULTRASTOR_DEBUG -#define ULTRASTOR_DEBUG (UD_ABORT|UD_CSIR|UD_RESET) -#endif - -#define VERSION "1.12" - -#define PACKED __attribute__((packed)) -#define ALIGNED(x) __attribute__((aligned(x))) - - -/* The 14F uses an array of 4-byte ints for its scatter/gather list. - The data can be unaligned, but need not be. It's easier to give - the list normal alignment since it doesn't need to fit into a - packed structure. */ - -typedef struct { - u32 address; - u32 num_bytes; -} ultrastor_sg_list; - - -/* MailBox SCSI Command Packet. Basic command structure for communicating - with controller. */ -struct mscp { - unsigned char opcode: 3; /* type of command */ - unsigned char xdir: 2; /* data transfer direction */ - unsigned char dcn: 1; /* disable disconnect */ - unsigned char ca: 1; /* use cache (if available) */ - unsigned char sg: 1; /* scatter/gather operation */ - unsigned char target_id: 3; /* target SCSI id */ - unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */ - unsigned char lun: 3; /* logical unit number */ - unsigned int transfer_data PACKED; /* transfer data pointer */ - unsigned int transfer_data_length PACKED; /* length in bytes */ - unsigned int command_link PACKED; /* for linking command chains */ - unsigned char scsi_command_link_id; /* identifies command in chain */ - unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */ - unsigned char length_of_sense_byte; - unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */ - unsigned char scsi_cdbs[12]; /* SCSI commands */ - unsigned char adapter_status; /* non-zero indicates HA error */ - unsigned char target_status; /* non-zero indicates target error */ - u32 sense_data PACKED; - /* The following fields are for software only. They are included in - the MSCP structure because they are associated with SCSI requests. */ - void (*done) (struct scsi_cmnd *); - struct scsi_cmnd *SCint; - ultrastor_sg_list sglist[ULTRASTOR_24F_MAX_SG]; /* use larger size for 24F */ -}; - - -/* Port addresses (relative to the base address) */ -#define U14F_PRODUCT_ID(port) ((port) + 0x4) -#define CONFIG(port) ((port) + 0x6) - -/* Port addresses relative to the doorbell base address. */ -#define LCL_DOORBELL_MASK(port) ((port) + 0x0) -#define LCL_DOORBELL_INTR(port) ((port) + 0x1) -#define SYS_DOORBELL_MASK(port) ((port) + 0x2) -#define SYS_DOORBELL_INTR(port) ((port) + 0x3) - - -/* Used to store configuration info read from config i/o registers. Most of - this is not used yet, but might as well save it. - - This structure also holds port addresses that are not at the same offset - on the 14F and 24F. - - This structure holds all data that must be duplicated to support multiple - adapters. */ - -static struct ultrastor_config -{ - unsigned short port_address; /* base address of card */ - unsigned short doorbell_address; /* base address of doorbell CSRs */ - unsigned short ogm_address; /* base address of OGM */ - unsigned short icm_address; /* base address of ICM */ - const void *bios_segment; - unsigned char interrupt: 4; - unsigned char dma_channel: 3; - unsigned char bios_drive_number: 1; - unsigned char heads; - unsigned char sectors; - unsigned char ha_scsi_id: 3; - unsigned char subversion: 4; - unsigned char revision; - /* The slot number is used to distinguish the 24F (slot != 0) from - the 14F and 34F (slot == 0). */ - unsigned char slot; - -#ifdef PRINT_U24F_VERSION - volatile int csir_done; -#endif - - /* A pool of MSCP structures for this adapter, and a bitmask of - busy structures. (If ULTRASTOR_14F_MAX_CMDS == 1, a 1 byte - busy flag is used instead.) */ - -#if ULTRASTOR_MAX_CMDS == 1 - unsigned char mscp_busy; -#else - unsigned long mscp_free; -#endif - volatile unsigned char aborted[ULTRASTOR_MAX_CMDS]; - struct mscp mscp[ULTRASTOR_MAX_CMDS]; -} config = {0}; - -/* Set this to 1 to reset the SCSI bus on error. */ -static int ultrastor_bus_reset; - - -/* Allowed BIOS base addresses (NULL indicates reserved) */ -static const void *const bios_segment_table[8] = { - NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000, - (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000, -}; - -/* Allowed IRQs for 14f */ -static const unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 }; - -/* Allowed DMA channels for 14f (0 indicates reserved) */ -static const unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 }; - -/* Head/sector mappings allowed by 14f */ -static const struct { - unsigned char heads; - unsigned char sectors; -} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 } }; - -#ifndef PORT_OVERRIDE -/* ??? A probe of address 0x310 screws up NE2000 cards */ -static const unsigned short ultrastor_ports_14f[] = { - 0x330, 0x340, /*0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140, -}; -#endif - -static void ultrastor_interrupt(void *); -static irqreturn_t do_ultrastor_interrupt(int, void *); -static inline void build_sg_list(struct mscp *, struct scsi_cmnd *SCpnt); - - -/* Always called with host lock held */ - -static inline int find_and_clear_bit_16(unsigned long *field) -{ - int rv; - - if (*field == 0) - panic("No free mscp"); - - asm volatile ( - "xorl %0,%0\n\t" - "0: bsfw %1,%w0\n\t" - "btr %0,%1\n\t" - "jnc 0b" - : "=&r" (rv), "+m" (*field) :); - - return rv; -} - -/* This has been re-implemented with the help of Richard Earnshaw, - and works with gcc-2.5.8 and gcc-2.6.0. - The instability noted by jfc below appears to be a bug in - gcc-2.5.x when compiling w/o optimization. --Caleb - - This asm is fragile: it doesn't work without the casts and it may - not work without optimization. Maybe I should add a swap builtin - to gcc. --jfc */ -static inline unsigned char xchgb(unsigned char reg, - volatile unsigned char *mem) -{ - __asm__ ("xchgb %0,%1" : "=q" (reg), "=m" (*mem) : "0" (reg)); - return reg; -} - -#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT) - -/* Always called with the host lock held */ -static void log_ultrastor_abort(struct ultrastor_config *config, - int command) -{ - static char fmt[80] = "abort %d (%x); MSCP free pool: %x;"; - int i; - - for (i = 0; i < ULTRASTOR_MAX_CMDS; i++) - { - fmt[20 + i*2] = ' '; - if (! (config->mscp_free & (1 << i))) - fmt[21 + i*2] = '0' + config->mscp[i].target_id; - else - fmt[21 + i*2] = '-'; - } - fmt[20 + ULTRASTOR_MAX_CMDS * 2] = '\n'; - fmt[21 + ULTRASTOR_MAX_CMDS * 2] = 0; - printk(fmt, command, &config->mscp[command], config->mscp_free); - -} -#endif - -static int ultrastor_14f_detect(struct scsi_host_template * tpnt) -{ - size_t i; - unsigned char in_byte, version_byte = 0; - struct config_1 { - unsigned char bios_segment: 3; - unsigned char removable_disks_as_fixed: 1; - unsigned char interrupt: 2; - unsigned char dma_channel: 2; - } config_1; - struct config_2 { - unsigned char ha_scsi_id: 3; - unsigned char mapping_mode: 2; - unsigned char bios_drive_number: 1; - unsigned char tfr_port: 2; - } config_2; - -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: called\n"); -#endif - - /* If a 24F has already been configured, don't look for a 14F. */ - if (config.bios_segment) - return FALSE; - -#ifdef PORT_OVERRIDE - if(!request_region(PORT_OVERRIDE, 0xc, "ultrastor")) { - printk("Ultrastor I/O space already in use\n"); - return FALSE; - }; - config.port_address = PORT_OVERRIDE; -#else - for (i = 0; i < ARRAY_SIZE(ultrastor_ports_14f); i++) { - if(!request_region(ultrastor_ports_14f[i], 0x0c, "ultrastor")) continue; - config.port_address = ultrastor_ports_14f[i]; -#endif - -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: testing port address %03X\n", config.port_address); -#endif - - in_byte = inb(U14F_PRODUCT_ID(config.port_address)); - if (in_byte != US14F_PRODUCT_ID_0) { -#if (ULTRASTOR_DEBUG & UD_DETECT) -# ifdef PORT_OVERRIDE - printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte); -# else - printk("US14F: detect: no adapter at port %03X\n", config.port_address); -# endif -#endif -#ifdef PORT_OVERRIDE - goto out_release_port; -#else - release_region(config.port_address, 0x0c); - continue; -#endif - } - in_byte = inb(U14F_PRODUCT_ID(config.port_address) + 1); - /* Only upper nibble is significant for Product ID 1 */ - if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) { -#if (ULTRASTOR_DEBUG & UD_DETECT) -# ifdef PORT_OVERRIDE - printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte); -# else - printk("US14F: detect: no adapter at port %03X\n", config.port_address); -# endif -#endif -#ifdef PORT_OVERRIDE - goto out_release_port; -#else - release_region(config.port_address, 0x0c); - continue; -#endif - } - version_byte = in_byte; -#ifndef PORT_OVERRIDE - break; - } - if (i == ARRAY_SIZE(ultrastor_ports_14f)) { -# if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: no port address found!\n"); -# endif - /* all ports probed already released - we can just go straight out */ - return FALSE; - } -#endif - -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: adapter found at port address %03X\n", - config.port_address); -#endif - - /* Set local doorbell mask to disallow bus reset unless - ultrastor_bus_reset is true. */ - outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(config.port_address)); - - /* All above tests passed, must be the right thing. Get some useful - info. */ - - /* Register the I/O space that we use */ - - *(char *)&config_1 = inb(CONFIG(config.port_address + 0)); - *(char *)&config_2 = inb(CONFIG(config.port_address + 1)); - config.bios_segment = bios_segment_table[config_1.bios_segment]; - config.doorbell_address = config.port_address; - config.ogm_address = config.port_address + 0x8; - config.icm_address = config.port_address + 0xC; - config.interrupt = interrupt_table_14f[config_1.interrupt]; - config.ha_scsi_id = config_2.ha_scsi_id; - config.heads = mapping_table[config_2.mapping_mode].heads; - config.sectors = mapping_table[config_2.mapping_mode].sectors; - config.bios_drive_number = config_2.bios_drive_number; - config.subversion = (version_byte & 0x0F); - if (config.subversion == U34F) - config.dma_channel = 0; - else - config.dma_channel = dma_channel_table_14f[config_1.dma_channel]; - - if (!config.bios_segment) { -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: not detected.\n"); -#endif - goto out_release_port; - } - - /* Final consistency check, verify previous info. */ - if (config.subversion != U34F) - if (!config.dma_channel || !(config_2.tfr_port & 0x2)) { -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: consistency check failed\n"); -#endif - goto out_release_port; - } - - /* If we were TRULY paranoid, we could issue a host adapter inquiry - command here and verify the data returned. But frankly, I'm - exhausted! */ - - /* Finally! Now I'm satisfied... */ -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: detect succeeded\n" - " Port address: %03X\n" - " BIOS segment: %05X\n" - " Interrupt: %u\n" - " DMA channel: %u\n" - " H/A SCSI ID: %u\n" - " Subversion: %u\n", - config.port_address, config.bios_segment, config.interrupt, - config.dma_channel, config.ha_scsi_id, config.subversion); -#endif - tpnt->this_id = config.ha_scsi_id; - tpnt->unchecked_isa_dma = (config.subversion != U34F); - -#if ULTRASTOR_MAX_CMDS > 1 - config.mscp_free = ~0; -#endif - - /* - * Brrr, &config.mscp[0].SCint->host) it is something magical.... - * XXX and FIXME - */ - if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", &config.mscp[0].SCint->device->host)) { - printk("Unable to allocate IRQ%u for UltraStor controller.\n", - config.interrupt); - goto out_release_port; - } - if (config.dma_channel && request_dma(config.dma_channel,"Ultrastor")) { - printk("Unable to allocate DMA channel %u for UltraStor controller.\n", - config.dma_channel); - free_irq(config.interrupt, NULL); - goto out_release_port; - } - tpnt->sg_tablesize = ULTRASTOR_14F_MAX_SG; - printk("UltraStor driver version" VERSION ". Using %d SG lists.\n", - ULTRASTOR_14F_MAX_SG); - - return TRUE; -out_release_port: - release_region(config.port_address, 0x0c); - return FALSE; -} - -static int ultrastor_24f_detect(struct scsi_host_template * tpnt) -{ - int i; - struct Scsi_Host * shpnt = NULL; - -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US24F: detect"); -#endif - - /* probe each EISA slot at slot address C80 */ - for (i = 1; i < 15; i++) - { - unsigned char config_1, config_2; - unsigned short addr = (i << 12) | ULTRASTOR_24F_PORT; - - if (inb(addr) != US24F_PRODUCT_ID_0 && - inb(addr+1) != US24F_PRODUCT_ID_1 && - inb(addr+2) != US24F_PRODUCT_ID_2) - continue; - - config.revision = inb(addr+3); - config.slot = i; - if (! (inb(addr+4) & 1)) - { -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("U24F: found disabled card in slot %u\n", i); -#endif - continue; - } -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("U24F: found card in slot %u\n", i); -#endif - config_1 = inb(addr + 5); - config.bios_segment = bios_segment_table[config_1 & 7]; - switch(config_1 >> 4) - { - case 1: - config.interrupt = 15; - break; - case 2: - config.interrupt = 14; - break; - case 4: - config.interrupt = 11; - break; - case 8: - config.interrupt = 10; - break; - default: - printk("U24F: invalid IRQ\n"); - return FALSE; - } - - /* BIOS addr set */ - /* base port set */ - config.port_address = addr; - config.doorbell_address = addr + 12; - config.ogm_address = addr + 0x17; - config.icm_address = addr + 0x1C; - config_2 = inb(addr + 7); - config.ha_scsi_id = config_2 & 7; - config.heads = mapping_table[(config_2 >> 3) & 3].heads; - config.sectors = mapping_table[(config_2 >> 3) & 3].sectors; -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US24F: detect: detect succeeded\n" - " Port address: %03X\n" - " BIOS segment: %05X\n" - " Interrupt: %u\n" - " H/A SCSI ID: %u\n", - config.port_address, config.bios_segment, - config.interrupt, config.ha_scsi_id); -#endif - tpnt->this_id = config.ha_scsi_id; - tpnt->unchecked_isa_dma = 0; - tpnt->sg_tablesize = ULTRASTOR_24F_MAX_SG; - - shpnt = scsi_register(tpnt, 0); - if (!shpnt) { - printk(KERN_WARNING "(ultrastor:) Could not register scsi device. Aborting registration.\n"); - free_irq(config.interrupt, do_ultrastor_interrupt); - return FALSE; - } - - if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", shpnt)) - { - printk("Unable to allocate IRQ%u for UltraStor controller.\n", - config.interrupt); - return FALSE; - } - - shpnt->irq = config.interrupt; - shpnt->dma_channel = config.dma_channel; - shpnt->io_port = config.port_address; - -#if ULTRASTOR_MAX_CMDS > 1 - config.mscp_free = ~0; -#endif - /* Mark ICM and OGM free */ - outb(0, addr + 0x16); - outb(0, addr + 0x1B); - - /* Set local doorbell mask to disallow bus reset unless - ultrastor_bus_reset is true. */ - outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(addr+12)); - outb(0x02, SYS_DOORBELL_MASK(addr+12)); - printk("UltraStor driver version " VERSION ". Using %d SG lists.\n", - tpnt->sg_tablesize); - return TRUE; - } - return FALSE; -} - -static int ultrastor_detect(struct scsi_host_template * tpnt) -{ - tpnt->proc_name = "ultrastor"; - return ultrastor_14f_detect(tpnt) || ultrastor_24f_detect(tpnt); -} - -static int ultrastor_release(struct Scsi_Host *shost) -{ - if (shost->irq) - free_irq(shost->irq, NULL); - if (shost->dma_channel != 0xff) - free_dma(shost->dma_channel); - if (shost->io_port && shost->n_io_port) - release_region(shost->io_port, shost->n_io_port); - scsi_unregister(shost); - return 0; -} - -static const char *ultrastor_info(struct Scsi_Host * shpnt) -{ - static char buf[64]; - - if (config.slot) - sprintf(buf, "UltraStor 24F SCSI @ Slot %u IRQ%u", - config.slot, config.interrupt); - else if (config.subversion) - sprintf(buf, "UltraStor 34F SCSI @ Port %03X BIOS %05X IRQ%u", - config.port_address, (int)config.bios_segment, - config.interrupt); - else - sprintf(buf, "UltraStor 14F SCSI @ Port %03X BIOS %05X IRQ%u DMA%u", - config.port_address, (int)config.bios_segment, - config.interrupt, config.dma_channel); - return buf; -} - -static inline void build_sg_list(struct mscp *mscp, struct scsi_cmnd *SCpnt) -{ - struct scatterlist *sg; - long transfer_length = 0; - int i, max; - - max = scsi_sg_count(SCpnt); - scsi_for_each_sg(SCpnt, sg, max, i) { - mscp->sglist[i].address = isa_page_to_bus(sg_page(sg)) + sg->offset; - mscp->sglist[i].num_bytes = sg->length; - transfer_length += sg->length; - } - mscp->number_of_sg_list = max; - mscp->transfer_data = isa_virt_to_bus(mscp->sglist); - /* ??? May not be necessary. Docs are unclear as to whether transfer - length field is ignored or whether it should be set to the total - number of bytes of the transfer. */ - mscp->transfer_data_length = transfer_length; -} - -static int ultrastor_queuecommand_lck(struct scsi_cmnd *SCpnt, - void (*done) (struct scsi_cmnd *)) -{ - struct mscp *my_mscp; -#if ULTRASTOR_MAX_CMDS > 1 - int mscp_index; -#endif - unsigned int status; - - /* Next test is for debugging; "can't happen" */ - if ((config.mscp_free & ((1U << ULTRASTOR_MAX_CMDS) - 1)) == 0) - panic("ultrastor_queuecommand: no free MSCP\n"); - mscp_index = find_and_clear_bit_16(&config.mscp_free); - - /* Has the command been aborted? */ - if (xchgb(0xff, &config.aborted[mscp_index]) != 0) - { - status = DID_ABORT << 16; - goto aborted; - } - - my_mscp = &config.mscp[mscp_index]; - - *(unsigned char *)my_mscp = OP_SCSI | (DTD_SCSI << 3); - - /* Tape drives don't work properly if the cache is used. The SCSI - READ command for a tape doesn't have a block offset, and the adapter - incorrectly assumes that all reads from the tape read the same - blocks. Results will depend on read buffer size and other disk - activity. - - ??? Which other device types should never use the cache? */ - my_mscp->ca = SCpnt->device->type != TYPE_TAPE; - my_mscp->target_id = SCpnt->device->id; - my_mscp->ch_no = 0; - my_mscp->lun = SCpnt->device->lun; - if (scsi_sg_count(SCpnt)) { - /* Set scatter/gather flag in SCSI command packet */ - my_mscp->sg = TRUE; - build_sg_list(my_mscp, SCpnt); - } else { - /* Unset scatter/gather flag in SCSI command packet */ - my_mscp->sg = FALSE; - my_mscp->transfer_data = isa_virt_to_bus(scsi_sglist(SCpnt)); - my_mscp->transfer_data_length = scsi_bufflen(SCpnt); - } - my_mscp->command_link = 0; /*???*/ - my_mscp->scsi_command_link_id = 0; /*???*/ - my_mscp->length_of_sense_byte = SCSI_SENSE_BUFFERSIZE; - my_mscp->length_of_scsi_cdbs = SCpnt->cmd_len; - memcpy(my_mscp->scsi_cdbs, SCpnt->cmnd, my_mscp->length_of_scsi_cdbs); - my_mscp->adapter_status = 0; - my_mscp->target_status = 0; - my_mscp->sense_data = isa_virt_to_bus(&SCpnt->sense_buffer); - my_mscp->done = done; - my_mscp->SCint = SCpnt; - SCpnt->host_scribble = (unsigned char *)my_mscp; - - /* Find free OGM slot. On 24F, look for OGM status byte == 0. - On 14F and 34F, wait for local interrupt pending flag to clear. - - FIXME: now we are using new_eh we should punt here and let the - midlayer sort it out */ - -retry: - if (config.slot) - while (inb(config.ogm_address - 1) != 0 && config.aborted[mscp_index] == 0xff) - barrier(); - - /* else??? */ - - while ((inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) && config.aborted[mscp_index] == 0xff) - barrier(); - - /* To avoid race conditions, keep the code to write to the adapter - atomic. This simplifies the abort code. Right now the - scsi mid layer has the host_lock already held - */ - - if (inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) - goto retry; - - status = xchgb(0, &config.aborted[mscp_index]); - if (status != 0xff) { - -#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT) - printk("USx4F: queuecommand: aborted\n"); -#if ULTRASTOR_MAX_CMDS > 1 - log_ultrastor_abort(&config, mscp_index); -#endif -#endif - status <<= 16; - - aborted: - set_bit(mscp_index, &config.mscp_free); - /* If the driver queues commands, call the done proc here. Otherwise - return an error. */ -#if ULTRASTOR_MAX_CMDS > 1 - SCpnt->result = status; - done(SCpnt); - return 0; -#else - return status; -#endif - } - - /* Store pointer in OGM address bytes */ - outl(isa_virt_to_bus(my_mscp), config.ogm_address); - - /* Issue OGM interrupt */ - if (config.slot) { - /* Write OGM command register on 24F */ - outb(1, config.ogm_address - 1); - outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address)); - } else { - outb(0x1, LCL_DOORBELL_INTR(config.doorbell_address)); - } - -#if (ULTRASTOR_DEBUG & UD_COMMAND) - printk("USx4F: queuecommand: returning\n"); -#endif - - return 0; -} - -static DEF_SCSI_QCMD(ultrastor_queuecommand) - -/* This code must deal with 2 cases: - - 1. The command has not been written to the OGM. In this case, set - the abort flag and return. - - 2. The command has been written to the OGM and is stuck somewhere in - the adapter. - - 2a. On a 24F, ask the adapter to abort the command. It will interrupt - when it does. - - 2b. Call the command's done procedure. - - */ - -static int ultrastor_abort(struct scsi_cmnd *SCpnt) -{ -#if ULTRASTOR_DEBUG & UD_ABORT - char out[108]; - unsigned char icm_status = 0, ogm_status = 0; - unsigned int icm_addr = 0, ogm_addr = 0; -#endif - unsigned int mscp_index; - unsigned char old_aborted; - unsigned long flags; - void (*done)(struct scsi_cmnd *); - struct Scsi_Host *host = SCpnt->device->host; - - if(config.slot) - return FAILED; /* Do not attempt an abort for the 24f */ - - /* Simple consistency checking */ - if(!SCpnt->host_scribble) - return FAILED; - - mscp_index = ((struct mscp *)SCpnt->host_scribble) - config.mscp; - if (mscp_index >= ULTRASTOR_MAX_CMDS) - panic("Ux4F aborting invalid MSCP"); - -#if ULTRASTOR_DEBUG & UD_ABORT - if (config.slot) - { - int port0 = (config.slot << 12) | 0xc80; - int i; - unsigned long flags; - - spin_lock_irqsave(host->host_lock, flags); - strcpy(out, "OGM %d:%x ICM %d:%x ports: "); - for (i = 0; i < 16; i++) - { - unsigned char p = inb(port0 + i); - out[28 + i * 3] = "0123456789abcdef"[p >> 4]; - out[29 + i * 3] = "0123456789abcdef"[p & 15]; - out[30 + i * 3] = ' '; - } - out[28 + i * 3] = '\n'; - out[29 + i * 3] = 0; - ogm_status = inb(port0 + 22); - ogm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 23)); - icm_status = inb(port0 + 27); - icm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 28)); - spin_unlock_irqrestore(host->host_lock, flags); - } - - /* First check to see if an interrupt is pending. I suspect the SiS - chipset loses interrupts. (I also suspect is mangles data, but - one bug at a time... */ - if (config.slot ? inb(config.icm_address - 1) == 2 : - (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1)) - { - printk("Ux4F: abort while completed command pending\n"); - - spin_lock_irqsave(host->host_lock, flags); - /* FIXME: Ewww... need to think about passing host around properly */ - ultrastor_interrupt(NULL); - spin_unlock_irqrestore(host->host_lock, flags); - return SUCCESS; - } -#endif - - old_aborted = xchgb(DID_ABORT, &config.aborted[mscp_index]); - - /* aborted == 0xff is the signal that queuecommand has not yet sent - the command. It will notice the new abort flag and fail. */ - if (old_aborted == 0xff) - return SUCCESS; - - /* On 24F, send an abort MSCP request. The adapter will interrupt - and the interrupt handler will call done. */ - if (config.slot && inb(config.ogm_address - 1) == 0) - { - unsigned long flags; - - spin_lock_irqsave(host->host_lock, flags); - outl(isa_virt_to_bus(&config.mscp[mscp_index]), config.ogm_address); - udelay(8); - outb(0x80, config.ogm_address - 1); - outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address)); -#if ULTRASTOR_DEBUG & UD_ABORT - log_ultrastor_abort(&config, mscp_index); - printk(out, ogm_status, ogm_addr, icm_status, icm_addr); -#endif - spin_unlock_irqrestore(host->host_lock, flags); - /* FIXME: add a wait for the abort to complete */ - return SUCCESS; - } - -#if ULTRASTOR_DEBUG & UD_ABORT - log_ultrastor_abort(&config, mscp_index); -#endif - - /* Can't request a graceful abort. Either this is not a 24F or - the OGM is busy. Don't free the command -- the adapter might - still be using it. Setting SCint = 0 causes the interrupt - handler to ignore the command. */ - - /* FIXME - devices that implement soft resets will still be running - the command after a bus reset. We would probably rather leave - the command in the queue. The upper level code will automatically - leave the command in the active state instead of requeueing it. ERY */ - -#if ULTRASTOR_DEBUG & UD_ABORT - if (config.mscp[mscp_index].SCint != SCpnt) - printk("abort: command mismatch, %p != %p\n", - config.mscp[mscp_index].SCint, SCpnt); -#endif - if (config.mscp[mscp_index].SCint == NULL) - return FAILED; - - if (config.mscp[mscp_index].SCint != SCpnt) panic("Bad abort"); - config.mscp[mscp_index].SCint = NULL; - done = config.mscp[mscp_index].done; - config.mscp[mscp_index].done = NULL; - SCpnt->result = DID_ABORT << 16; - - /* Take the host lock to guard against scsi layer re-entry */ - done(SCpnt); - - /* Need to set a timeout here in case command never completes. */ - return SUCCESS; -} - -static int ultrastor_host_reset(struct scsi_cmnd * SCpnt) -{ - unsigned long flags; - int i; - struct Scsi_Host *host = SCpnt->device->host; - -#if (ULTRASTOR_DEBUG & UD_RESET) - printk("US14F: reset: called\n"); -#endif - - if(config.slot) - return FAILED; - - spin_lock_irqsave(host->host_lock, flags); - /* Reset the adapter and SCSI bus. The SCSI bus reset can be - inhibited by clearing ultrastor_bus_reset before probe. */ - outb(0xc0, LCL_DOORBELL_INTR(config.doorbell_address)); - if (config.slot) - { - outb(0, config.ogm_address - 1); - outb(0, config.icm_address - 1); - } - -#if ULTRASTOR_MAX_CMDS == 1 - if (config.mscp_busy && config.mscp->done && config.mscp->SCint) - { - config.mscp->SCint->result = DID_RESET << 16; - config.mscp->done(config.mscp->SCint); - } - config.mscp->SCint = 0; -#else - for (i = 0; i < ULTRASTOR_MAX_CMDS; i++) - { - if (! (config.mscp_free & (1 << i)) && - config.mscp[i].done && config.mscp[i].SCint) - { - config.mscp[i].SCint->result = DID_RESET << 16; - config.mscp[i].done(config.mscp[i].SCint); - config.mscp[i].done = NULL; - } - config.mscp[i].SCint = NULL; - } -#endif - - /* FIXME - if the device implements soft resets, then the command - will still be running. ERY - - Even bigger deal with new_eh! - */ - - memset((unsigned char *)config.aborted, 0, sizeof config.aborted); -#if ULTRASTOR_MAX_CMDS == 1 - config.mscp_busy = 0; -#else - config.mscp_free = ~0; -#endif - - spin_unlock_irqrestore(host->host_lock, flags); - return SUCCESS; - -} - -int ultrastor_biosparam(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int * dkinfo) -{ - int size = capacity; - unsigned int s = config.heads * config.sectors; - - dkinfo[0] = config.heads; - dkinfo[1] = config.sectors; - dkinfo[2] = size / s; /* Ignore partial cylinders */ -#if 0 - if (dkinfo[2] > 1024) - dkinfo[2] = 1024; -#endif - return 0; -} - -static void ultrastor_interrupt(void *dev_id) -{ - unsigned int status; -#if ULTRASTOR_MAX_CMDS > 1 - unsigned int mscp_index; -#endif - struct mscp *mscp; - void (*done) (struct scsi_cmnd *); - struct scsi_cmnd *SCtmp; - -#if ULTRASTOR_MAX_CMDS == 1 - mscp = &config.mscp[0]; -#else - mscp = (struct mscp *)isa_bus_to_virt(inl(config.icm_address)); - mscp_index = mscp - config.mscp; - if (mscp_index >= ULTRASTOR_MAX_CMDS) { - printk("Ux4F interrupt: bad MSCP address %x\n", (unsigned int) mscp); - /* A command has been lost. Reset and report an error - for all commands. */ - ultrastor_host_reset(dev_id); - return; - } -#endif - - /* Clean ICM slot (set ICMINT bit to 0) */ - if (config.slot) { - unsigned char icm_status = inb(config.icm_address - 1); -#if ULTRASTOR_DEBUG & (UD_INTERRUPT|UD_ERROR|UD_ABORT) - if (icm_status != 1 && icm_status != 2) - printk("US24F: ICM status %x for MSCP %d (%x)\n", icm_status, - mscp_index, (unsigned int) mscp); -#endif - /* The manual says clear interrupt then write 0 to ICM status. - This seems backwards, but I'll do it anyway. --jfc */ - outb(2, SYS_DOORBELL_INTR(config.doorbell_address)); - outb(0, config.icm_address - 1); - if (icm_status == 4) { - printk("UltraStor abort command failed\n"); - return; - } - if (icm_status == 3) { - void (*done)(struct scsi_cmnd *) = mscp->done; - if (done) { - mscp->done = NULL; - mscp->SCint->result = DID_ABORT << 16; - done(mscp->SCint); - } - return; - } - } else { - outb(1, SYS_DOORBELL_INTR(config.doorbell_address)); - } - - SCtmp = mscp->SCint; - mscp->SCint = NULL; - - if (!SCtmp) - { -#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT) - printk("MSCP %d (%x): no command\n", mscp_index, (unsigned int) mscp); -#endif -#if ULTRASTOR_MAX_CMDS == 1 - config.mscp_busy = FALSE; -#else - set_bit(mscp_index, &config.mscp_free); -#endif - config.aborted[mscp_index] = 0; - return; - } - - /* Save done locally and zero before calling. This is needed as - once we call done, we may get another command queued before this - interrupt service routine can return. */ - done = mscp->done; - mscp->done = NULL; - - /* Let the higher levels know that we're done */ - switch (mscp->adapter_status) - { - case 0: - status = DID_OK << 16; - break; - case 0x01: /* invalid command */ - case 0x02: /* invalid parameters */ - case 0x03: /* invalid data list */ - default: - status = DID_ERROR << 16; - break; - case 0x84: /* SCSI bus abort */ - status = DID_ABORT << 16; - break; - case 0x91: - status = DID_TIME_OUT << 16; - break; - } - - SCtmp->result = status | mscp->target_status; - - SCtmp->host_scribble = NULL; - - /* Free up mscp block for next command */ -#if ULTRASTOR_MAX_CMDS == 1 - config.mscp_busy = FALSE; -#else - set_bit(mscp_index, &config.mscp_free); -#endif - -#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT) - if (config.aborted[mscp_index]) - printk("Ux4 interrupt: MSCP %d (%x) aborted = %d\n", - mscp_index, (unsigned int) mscp, config.aborted[mscp_index]); -#endif - config.aborted[mscp_index] = 0; - - if (done) - done(SCtmp); - else - printk("US14F: interrupt: unexpected interrupt\n"); - - if (config.slot ? inb(config.icm_address - 1) : - (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1)) -#if (ULTRASTOR_DEBUG & UD_MULTI_CMD) - printk("Ux4F: multiple commands completed\n"); -#else - ; -#endif - -#if (ULTRASTOR_DEBUG & UD_INTERRUPT) - printk("USx4F: interrupt: returning\n"); -#endif -} - -static irqreturn_t do_ultrastor_interrupt(int irq, void *dev_id) -{ - unsigned long flags; - struct Scsi_Host *dev = dev_id; - - spin_lock_irqsave(dev->host_lock, flags); - ultrastor_interrupt(dev_id); - spin_unlock_irqrestore(dev->host_lock, flags); - return IRQ_HANDLED; -} - -MODULE_LICENSE("GPL"); - -static struct scsi_host_template driver_template = { - .name = "UltraStor 14F/24F/34F", - .detect = ultrastor_detect, - .release = ultrastor_release, - .info = ultrastor_info, - .queuecommand = ultrastor_queuecommand, - .eh_abort_handler = ultrastor_abort, - .eh_host_reset_handler = ultrastor_host_reset, - .bios_param = ultrastor_biosparam, - .can_queue = ULTRASTOR_MAX_CMDS, - .sg_tablesize = ULTRASTOR_14F_MAX_SG, - .cmd_per_lun = ULTRASTOR_MAX_CMDS_PER_LUN, - .unchecked_isa_dma = 1, - .use_clustering = ENABLE_CLUSTERING, -}; -#include "scsi_module.c" diff --git a/drivers/scsi/ultrastor.h b/drivers/scsi/ultrastor.h deleted file mode 100644 index 165c18b5cf5f..000000000000 --- a/drivers/scsi/ultrastor.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ultrastor.c (C) 1991 David B. Gentzel - * Low-level scsi driver for UltraStor 14F - * by David B. Gentzel, Whitfield Software Services, Carnegie, PA - * (gentzel@nova.enet.dec.com) - * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu) - * 24F support by John F. Carr (jfc@athena.mit.edu) - * John's work modified by Caleb Epstein (cae@jpmorgan.com) and - * Eric Youngdale (eric@tantalus.nrl.navy.mil). - * Thanks to UltraStor for providing the necessary documentation - */ - -#ifndef _ULTRASTOR_H -#define _ULTRASTOR_H - -static int ultrastor_detect(struct scsi_host_template *); -static const char *ultrastor_info(struct Scsi_Host *shpnt); -static int ultrastor_queuecommand(struct Scsi_Host *, struct scsi_cmnd *); -static int ultrastor_abort(struct scsi_cmnd *); -static int ultrastor_host_reset(struct scsi_cmnd *); -static int ultrastor_biosparam(struct scsi_device *, struct block_device *, - sector_t, int *); - - -#define ULTRASTOR_14F_MAX_SG 16 -#define ULTRASTOR_24F_MAX_SG 33 - -#define ULTRASTOR_MAX_CMDS_PER_LUN 5 -#define ULTRASTOR_MAX_CMDS 16 - -#define ULTRASTOR_24F_PORT 0xC80 - - -#ifdef ULTRASTOR_PRIVATE - -#define UD_ABORT 0x0001 -#define UD_COMMAND 0x0002 -#define UD_DETECT 0x0004 -#define UD_INTERRUPT 0x0008 -#define UD_RESET 0x0010 -#define UD_MULTI_CMD 0x0020 -#define UD_CSIR 0x0040 -#define UD_ERROR 0x0080 - -/* #define PORT_OVERRIDE 0x330 */ - -/* Values for the PRODUCT_ID ports for the 14F */ -#define US14F_PRODUCT_ID_0 0x56 -#define US14F_PRODUCT_ID_1 0x40 /* NOTE: Only upper nibble is used */ - -#define US24F_PRODUCT_ID_0 0x56 -#define US24F_PRODUCT_ID_1 0x63 -#define US24F_PRODUCT_ID_2 0x02 - -/* Subversion values */ -#define U14F 0 -#define U34F 1 - -/* MSCP field values */ - -/* Opcode */ -#define OP_HOST_ADAPTER 0x1 -#define OP_SCSI 0x2 -#define OP_RESET 0x4 - -/* Date Transfer Direction */ -#define DTD_SCSI 0x0 -#define DTD_IN 0x1 -#define DTD_OUT 0x2 -#define DTD_NONE 0x3 - -/* Host Adapter command subcodes */ -#define HA_CMD_INQUIRY 0x1 -#define HA_CMD_SELF_DIAG 0x2 -#define HA_CMD_READ_BUFF 0x3 -#define HA_CMD_WRITE_BUFF 0x4 - -#endif - -#endif From 24cbf0f799d3ff8e1f1f6417e9040933acd4b38d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 19 Sep 2016 08:50:50 -0700 Subject: [PATCH 166/173] scsi: u14-34f: remove from tree The driver has not seen any maintainer activity or other work that wasn't tree wide conversion or clenaups in the entire history of the git tree. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinicke Signed-off-by: Martin K. Petersen --- Documentation/scsi/scsi-parameters.txt | 3 - MAINTAINERS | 6 - drivers/scsi/Kconfig | 49 - drivers/scsi/Makefile | 1 - drivers/scsi/u14-34f.c | 1971 ------------------------ 5 files changed, 2030 deletions(-) delete mode 100644 drivers/scsi/u14-34f.c diff --git a/Documentation/scsi/scsi-parameters.txt b/Documentation/scsi/scsi-parameters.txt index 2135ff4d1099..083bd934218a 100644 --- a/Documentation/scsi/scsi-parameters.txt +++ b/Documentation/scsi/scsi-parameters.txt @@ -123,8 +123,5 @@ parameters may be changed at runtime by the command See comment before function dc390_setup() in drivers/scsi/tmscsim.c. - u14-34f= [HW,SCSI] UltraStor 14F/34F SCSI host adapter - See header of drivers/scsi/u14-34f.c. - wd33c93= [HW,SCSI] See header of drivers/scsi/wd33c93.c. diff --git a/MAINTAINERS b/MAINTAINERS index fdcce6b52cba..6ca763fe0346 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11967,12 +11967,6 @@ S: Maintained F: drivers/tc/ F: include/linux/tc.h -U14-34F SCSI DRIVER -M: Dario Ballabio -L: linux-scsi@vger.kernel.org -S: Maintained -F: drivers/scsi/u14-34f.c - UBI FILE SYSTEM (UBIFS) M: Richard Weinberger M: Artem Bityutskiy diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 57cf77f84ca3..69785315eca3 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1376,55 +1376,6 @@ config SCSI_T128 To compile this driver as a module, choose M here: the module will be called t128. -config SCSI_U14_34F - tristate "UltraStor 14F/34F support" - depends on ISA && SCSI && ISA_DMA_API - ---help--- - This is support for the UltraStor 14F and 34F SCSI-2 host adapters. - The source at contains some - information about this hardware. If the driver doesn't work out of - the box, you may have to change some settings in - . Read the SCSI-HOWTO, available from - . Note that there is also - another driver for the same hardware: "UltraStor SCSI support", - below. You should say Y to both only if you want 24F support as - well. - - To compile this driver as a module, choose M here: the - module will be called u14-34f. - -config SCSI_U14_34F_TAGGED_QUEUE - bool "enable tagged command queueing" - depends on SCSI_U14_34F - help - This is a feature of SCSI-2 which improves performance: the host - adapter can send several SCSI commands to a device's queue even if - previous commands haven't finished yet. - This is equivalent to the "u14-34f=tc:y" boot option. - -config SCSI_U14_34F_LINKED_COMMANDS - bool "enable elevator sorting" - depends on SCSI_U14_34F - help - This option enables elevator sorting for all probed SCSI disks and - CD-ROMs. It definitely reduces the average seek distance when doing - random seeks, but this does not necessarily result in a noticeable - performance improvement: your mileage may vary... - This is equivalent to the "u14-34f=lc:y" boot option. - -config SCSI_U14_34F_MAX_TAGS - int "maximum number of queued commands" - depends on SCSI_U14_34F - default "8" - help - This specifies how many SCSI commands can be maximally queued for - each probed SCSI device. You should reduce the default value of 8 - only if you have disks with buggy or limited tagged command support. - Minimum is 2 and maximum is 14. This value is also the window size - used by the elevator sorting option above. The effective value used - by the driver for each probed SCSI device is reported at boot time. - This is equivalent to the "u14-34f=mq:8" boot option. - config SCSI_NSP32 tristate "Workbit NinjaSCSI-32Bi/UDE support" depends on PCI && SCSI && !64BIT diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 2e7d9efd86af..921cf07e9b7b 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -61,7 +61,6 @@ obj-$(CONFIG_SCSI_SIM710) += 53c700.o sim710.o obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o obj-$(CONFIG_SCSI_DPT_I2O) += dpt_i2o.o -obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o obj-$(CONFIG_SCSI_ARCMSR) += arcmsr/ obj-$(CONFIG_SCSI_AHA152X) += aha152x.o obj-$(CONFIG_SCSI_AHA1542) += aha1542.o diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c deleted file mode 100644 index 14eb50b95a1e..000000000000 --- a/drivers/scsi/u14-34f.c +++ /dev/null @@ -1,1971 +0,0 @@ -/* - * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters. - * - * 03 Jun 2003 Rev. 8.10 for linux-2.5.70 - * + Update for new IRQ API. - * + Use "goto" when appropriate. - * + Drop u14-34f.h. - * + Update for new module_param API. - * + Module parameters can now be specified only in the - * same format as the kernel boot options. - * - * boot option old module param - * ----------- ------------------ - * addr,... io_port=addr,... - * lc:[y|n] linked_comm=[1|0] - * mq:xx max_queue_depth=xx - * tm:[0|1|2] tag_mode=[0|1|2] - * et:[y|n] ext_tran=[1|0] - * of:[y|n] have_old_firmware=[1|0] - * - * A valid example using the new parameter format is: - * modprobe u14-34f "u14-34f=0x340,0x330,lc:y,tm:0,mq:4" - * - * which is equivalent to the old format: - * modprobe u14-34f io_port=0x340,0x330 linked_comm=1 tag_mode=0 \ - * max_queue_depth=4 - * - * With actual module code, u14-34f and u14_34f are equivalent - * as module parameter names. - * - * 12 Feb 2003 Rev. 8.04 for linux 2.5.60 - * + Release irq before calling scsi_register. - * - * 12 Nov 2002 Rev. 8.02 for linux 2.5.47 - * + Release driver_lock before calling scsi_register. - * - * 11 Nov 2002 Rev. 8.01 for linux 2.5.47 - * + Fixed bios_param and scsicam_bios_param calling parameters. - * - * 28 Oct 2002 Rev. 8.00 for linux 2.5.44-ac4 - * + Use new tcq and adjust_queue_depth api. - * + New command line option (tm:[0-2]) to choose the type of tags: - * 0 -> disable tagging ; 1 -> simple tags ; 2 -> ordered tags. - * Default is tm:0 (tagged commands disabled). - * For compatibility the "tc:" option is an alias of the "tm:" - * option; tc:n is equivalent to tm:0 and tc:y is equivalent to - * tm:1. - * - * 10 Oct 2002 Rev. 7.70 for linux 2.5.42 - * + Foreport from revision 6.70. - * - * 25 Jun 2002 Rev. 6.70 for linux 2.4.19 - * + Fixed endian-ness problem due to bitfields. - * - * 21 Feb 2002 Rev. 6.52 for linux 2.4.18 - * + Backport from rev. 7.22 (use io_request_lock). - * - * 20 Feb 2002 Rev. 7.22 for linux 2.5.5 - * + Remove any reference to virt_to_bus(). - * + Fix pio hang while detecting multiple HBAs. - * - * 01 Jan 2002 Rev. 7.20 for linux 2.5.1 - * + Use the dynamic DMA mapping API. - * - * 19 Dec 2001 Rev. 7.02 for linux 2.5.1 - * + Use SCpnt->sc_data_direction if set. - * + Use sglist.page instead of sglist.address. - * - * 11 Dec 2001 Rev. 7.00 for linux 2.5.1 - * + Use host->host_lock instead of io_request_lock. - * - * 1 May 2001 Rev. 6.05 for linux 2.4.4 - * + Fix data transfer direction for opcode SEND_CUE_SHEET (0x5d) - * - * 25 Jan 2001 Rev. 6.03 for linux 2.4.0 - * + "check_region" call replaced by "request_region". - * - * 22 Nov 2000 Rev. 6.02 for linux 2.4.0-test11 - * + Removed old scsi error handling support. - * + The obsolete boot option flag eh:n is silently ignored. - * + Removed error messages while a disk drive is powered up at - * boot time. - * + Improved boot messages: all tagged capable device are - * indicated as "tagged". - * - * 16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18 - * + Updated to the new __setup interface for boot command line options. - * + When loaded as a module, accepts the new parameter boot_options - * which value is a string with the same format of the kernel boot - * command line options. A valid example is: - * modprobe u14-34f 'boot_options="0x230,0x340,lc:y,mq:4"' - * - * 22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11 - * + Removed pre-2.2 source code compatibility. - * - * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111 - * Added command line option (et:[y|n]) to use the existing - * translation (returned by scsicam_bios_param) as disk geometry. - * The default is et:n, which uses the disk geometry jumpered - * on the board. - * The default value et:n is compatible with all previous revisions - * of this driver. - * - * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104 - * Increased busy timeout from 10 msec. to 200 msec. while - * processing interrupts. - * - * 18 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102 - * Improved abort handling during the eh recovery process. - * - * 13 May 1998 Rev. 4.30 for linux 2.0.33 and 2.1.101 - * The driver is now fully SMP safe, including the - * abort and reset routines. - * Added command line options (eh:[y|n]) to choose between - * new_eh_code and the old scsi code. - * If linux version >= 2.1.101 the default is eh:y, while the eh - * option is ignored for previous releases and the old scsi code - * is used. - * - * 18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97 - * Reworked interrupt handler. - * - * 11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95 - * Major reliability improvement: when a batch with overlapping - * requests is detected, requests are queued one at a time - * eliminating any possible board or drive reordering. - * - * 10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95 - * Improved SMP support (if linux version >= 2.1.95). - * - * 9 Apr 1998 rev. 4.03 for linux 2.0.33 and 2.1.94 - * Performance improvement: when sequential i/o is detected, - * always use direct sort instead of reverse sort. - * - * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92 - * io_port is now unsigned long. - * - * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88 - * Use new scsi error handling code (if linux version >= 2.1.88). - * Use new interrupt code. - * - * 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55 - * Use of udelay inside the wait loops to avoid timeout - * problems with fast cpus. - * Removed check about useless calls to the interrupt service - * routine (reported on SMP systems only). - * At initialization time "sorted/unsorted" is displayed instead - * of "linked/unlinked" to reinforce the fact that "linking" is - * nothing but "elevator sorting" in the actual implementation. - * - * 17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38 - * Use of serial_number_at_timeout in abort and reset processing. - * Use of the __initfunc and __initdata macro in setup code. - * Minor cleanups in the list_statistics code. - * - * 24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26 - * When loading as a module, parameter passing is now supported - * both in 2.0 and in 2.1 style. - * Fixed data transfer direction for some SCSI opcodes. - * Immediate acknowledge to request sense commands. - * Linked commands to each disk device are now reordered by elevator - * sorting. Rare cases in which reordering of write requests could - * cause wrong results are managed. - * - * 18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28 - * Added command line options to enable/disable linked commands - * (lc:[y|n]), old firmware support (of:[y|n]) and to set the max - * queue depth (mq:xx). Default is "u14-34f=lc:n,of:n,mq:8". - * Improved command linking. - * - * 8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27 - * Added linked command support. - * - * 3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27 - * Added queue depth adjustment. - * - * 22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26 - * The list of i/o ports to be probed can be overwritten by the - * "u14-34f=port0,port1,...." boot command line option. - * Scatter/gather lists are now allocated by a number of kmalloc - * calls, in order to avoid the previous size limit of 64Kb. - * - * 16 Nov 1996 rev. 2.20 for linux 2.1.10 and 2.0.25 - * Added multichannel support. - * - * 27 Sep 1996 rev. 2.12 for linux 2.1.0 - * Portability cleanups (virtual/bus addressing, little/big endian - * support). - * - * 09 Jul 1996 rev. 2.11 for linux 2.0.4 - * "Data over/under-run" no longer implies a redo on all targets. - * Number of internal retries is now limited. - * - * 16 Apr 1996 rev. 2.10 for linux 1.3.90 - * New argument "reset_flags" to the reset routine. - * - * 21 Jul 1995 rev. 2.02 for linux 1.3.11 - * Fixed Data Transfer Direction for some SCSI commands. - * - * 13 Jun 1995 rev. 2.01 for linux 1.2.10 - * HAVE_OLD_UX4F_FIRMWARE should be defined for U34F boards when - * the firmware prom is not the latest one (28008-006). - * - * 11 Mar 1995 rev. 2.00 for linux 1.2.0 - * Fixed a bug which prevented media change detection for removable - * disk drives. - * - * 23 Feb 1995 rev. 1.18 for linux 1.1.94 - * Added a check for scsi_register returning NULL. - * - * 11 Feb 1995 rev. 1.17 for linux 1.1.91 - * U14F qualified to run with 32 sglists. - * Now DEBUG_RESET is disabled by default. - * - * 9 Feb 1995 rev. 1.16 for linux 1.1.90 - * Use host->wish_block instead of host->block. - * - * 8 Feb 1995 rev. 1.15 for linux 1.1.89 - * Cleared target_time_out counter while performing a reset. - * - * 28 Jan 1995 rev. 1.14 for linux 1.1.86 - * Added module support. - * Log and do a retry when a disk drive returns a target status - * different from zero on a recovered error. - * Auto detects if U14F boards have an old firmware revision. - * Max number of scatter/gather lists set to 16 for all boards - * (most installation run fine using 33 sglists, while other - * has problems when using more than 16). - * - * 16 Jan 1995 rev. 1.13 for linux 1.1.81 - * Display a message if check_region detects a port address - * already in use. - * - * 15 Dec 1994 rev. 1.12 for linux 1.1.74 - * The host->block flag is set for all the detected ISA boards. - * - * 30 Nov 1994 rev. 1.11 for linux 1.1.68 - * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only. - * Added optional support for using a single board at a time. - * - * 14 Nov 1994 rev. 1.10 for linux 1.1.63 - * - * 28 Oct 1994 rev. 1.09 for linux 1.1.58 Final BETA release. - * 16 Jul 1994 rev. 1.00 for linux 1.1.29 Initial ALPHA release. - * - * This driver is a total replacement of the original UltraStor - * scsi driver, but it supports ONLY the 14F and 34F boards. - * It can be configured in the same kernel in which the original - * ultrastor driver is configured to allow the original U24F - * support. - * - * Multiple U14F and/or U34F host adapters are supported. - * - * Copyright (C) 1994-2003 Dario Ballabio (ballabio_dario@emc.com) - * - * Alternate email: dario.ballabio@inwind.it, dario.ballabio@tiscalinet.it - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that redistributions of source - * code retain the above copyright notice and this comment without - * modification. - * - * WARNING: if your 14/34F board has an old firmware revision (see below) - * you must change "#undef" into "#define" in the following - * statement. - */ -#undef HAVE_OLD_UX4F_FIRMWARE -/* - * The UltraStor 14F, 24F, and 34F are a family of intelligent, high - * performance SCSI-2 host adapters. - * Here is the scoop on the various models: - * - * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation. - * 24F - EISA Bus Master HA with floppy support and WD1003 emulation. - * 34F - VESA Local-Bus Bus Master HA (no WD1003 emulation). - * - * This code has been tested with up to two U14F boards, using both - * firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware - * 28004-006/38004-005 (BIOS rev. 2.01). - * - * The latest firmware is required in order to get reliable operations when - * clustering is enabled. ENABLE_CLUSTERING provides a performance increase - * up to 50% on sequential access. - * - * Since the struct scsi_host_template structure is shared among all 14F and 34F, - * the last setting of use_clustering is in effect for all of these boards. - * - * Here a sample configuration using two U14F boards: - * - U14F0: ISA 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, MB 16, of:n, lc:y, mq:8. - U14F1: ISA 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, MB 16, of:n, lc:y, mq:8. - * - * The boot controller must have its BIOS enabled, while other boards can - * have their BIOS disabled, or enabled to an higher address. - * Boards are named Ux4F0, Ux4F1..., according to the port address order in - * the io_port[] array. - * - * The following facts are based on real testing results (not on - * documentation) on the above U14F board. - * - * - The U14F board should be jumpered for bus on time less or equal to 7 - * microseconds, while the default is 11 microseconds. This is order to - * get acceptable performance while using floppy drive and hard disk - * together. The jumpering for 7 microseconds is: JP13 pin 15-16, - * JP14 pin 7-8 and pin 9-10. - * The reduction has a little impact on scsi performance. - * - * - If scsi bus length exceeds 3m., the scsi bus speed needs to be reduced - * from 10Mhz to 5Mhz (do this by inserting a jumper on JP13 pin 7-8). - * - * - If U14F on board firmware is older than 28004-006/38004-005, - * the U14F board is unable to provide reliable operations if the scsi - * request length exceeds 16Kbyte. When this length is exceeded the - * behavior is: - * - adapter_status equal 0x96 or 0xa3 or 0x93 or 0x94; - * - adapter_status equal 0 and target_status equal 2 on for all targets - * in the next operation following the reset. - * This sequence takes a long time (>3 seconds), so in the meantime - * the SD_TIMEOUT in sd.c could expire giving rise to scsi aborts - * (SD_TIMEOUT has been increased from 3 to 6 seconds in 1.1.31). - * Because of this I had to DISABLE_CLUSTERING and to work around the - * bus reset in the interrupt service routine, returning DID_BUS_BUSY - * so that the operations are retried without complains from the scsi.c - * code. - * Any reset of the scsi bus is going to kill tape operations, since - * no retry is allowed for tapes. Bus resets are more likely when the - * scsi bus is under heavy load. - * Requests using scatter/gather have a maximum length of 16 x 1024 bytes - * when DISABLE_CLUSTERING is in effect, but unscattered requests could be - * larger than 16Kbyte. - * - * The new firmware has fixed all the above problems. - * - * For U34F boards the latest bios prom is 38008-002 (BIOS rev. 2.01), - * the latest firmware prom is 28008-006. Older firmware 28008-005 has - * problems when using more than 16 scatter/gather lists. - * - * The list of i/o ports to be probed can be totally replaced by the - * boot command line option: "u14-34f=port0,port1,port2,...", where the - * port0, port1... arguments are ISA/VESA addresses to be probed. - * For example using "u14-34f=0x230,0x340", the driver probes only the two - * addresses 0x230 and 0x340 in this order; "u14-34f=0" totally disables - * this driver. - * - * After the optional list of detection probes, other possible command line - * options are: - * - * et:y use disk geometry returned by scsicam_bios_param; - * et:n use disk geometry jumpered on the board; - * lc:y enables linked commands; - * lc:n disables linked commands; - * tm:0 disables tagged commands (same as tc:n); - * tm:1 use simple queue tags (same as tc:y); - * tm:2 use ordered queue tags (same as tc:2); - * of:y enables old firmware support; - * of:n disables old firmware support; - * mq:xx set the max queue depth to the value xx (2 <= xx <= 8). - * - * The default value is: "u14-34f=lc:n,of:n,mq:8,tm:0,et:n". - * An example using the list of detection probes could be: - * "u14-34f=0x230,0x340,lc:y,tm:2,of:n,mq:4,et:n". - * - * When loading as a module, parameters can be specified as well. - * The above example would be (use 1 in place of y and 0 in place of n): - * - * modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \ - * max_queue_depth=4 ext_tran=0 tag_mode=2 - * - * ---------------------------------------------------------------------------- - * In this implementation, linked commands are designed to work with any DISK - * or CD-ROM, since this linking has only the intent of clustering (time-wise) - * and reordering by elevator sorting commands directed to each device, - * without any relation with the actual SCSI protocol between the controller - * and the device. - * If Q is the queue depth reported at boot time for each device (also named - * cmds/lun) and Q > 2, whenever there is already an active command to the - * device all other commands to the same device (up to Q-1) are kept waiting - * in the elevator sorting queue. When the active command completes, the - * commands in this queue are sorted by sector address. The sort is chosen - * between increasing or decreasing by minimizing the seek distance between - * the sector of the commands just completed and the sector of the first - * command in the list to be sorted. - * Trivial math assures that the unsorted average seek distance when doing - * random seeks over S sectors is S/3. - * When (Q-1) requests are uniformly distributed over S sectors, the average - * distance between two adjacent requests is S/((Q-1) + 1), so the sorted - * average seek distance for (Q-1) random requests over S sectors is S/Q. - * The elevator sorting hence divides the seek distance by a factor Q/3. - * The above pure geometric remarks are valid in all cases and the - * driver effectively reduces the seek distance by the predicted factor - * when there are Q concurrent read i/o operations on the device, but this - * does not necessarily results in a noticeable performance improvement: - * your mileage may vary.... - * - * Note: command reordering inside a batch of queued commands could cause - * wrong results only if there is at least one write request and the - * intersection (sector-wise) of all requests is not empty. - * When the driver detects a batch including overlapping requests - * (a really rare event) strict serial (pid) order is enforced. - * ---------------------------------------------------------------------------- - * - * The boards are named Ux4F0, Ux4F1,... according to the detection order. - * - * In order to support multiple ISA boards in a reliable way, - * the driver sets host->wish_block = TRUE for all ISA boards. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static int u14_34f_detect(struct scsi_host_template *); -static int u14_34f_release(struct Scsi_Host *); -static int u14_34f_queuecommand(struct Scsi_Host *, struct scsi_cmnd *); -static int u14_34f_eh_abort(struct scsi_cmnd *); -static int u14_34f_eh_host_reset(struct scsi_cmnd *); -static int u14_34f_bios_param(struct scsi_device *, struct block_device *, - sector_t, int *); -static int u14_34f_slave_configure(struct scsi_device *); - -static struct scsi_host_template driver_template = { - .name = "UltraStor 14F/34F rev. 8.10.00 ", - .detect = u14_34f_detect, - .release = u14_34f_release, - .queuecommand = u14_34f_queuecommand, - .eh_abort_handler = u14_34f_eh_abort, - .eh_host_reset_handler = u14_34f_eh_host_reset, - .bios_param = u14_34f_bios_param, - .slave_configure = u14_34f_slave_configure, - .this_id = 7, - .unchecked_isa_dma = 1, - .use_clustering = ENABLE_CLUSTERING, - }; - -#if !defined(__BIG_ENDIAN_BITFIELD) && !defined(__LITTLE_ENDIAN_BITFIELD) -#error "Adjust your defines" -#endif - -/* Values for the PRODUCT_ID ports for the 14/34F */ -#define PRODUCT_ID1 0x56 -#define PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */ - -/* Subversion values */ -#define ISA 0 -#define ESA 1 - -#define OP_HOST_ADAPTER 0x1 -#define OP_SCSI 0x2 -#define OP_RESET 0x4 -#define DTD_SCSI 0x0 -#define DTD_IN 0x1 -#define DTD_OUT 0x2 -#define DTD_NONE 0x3 -#define HA_CMD_INQUIRY 0x1 -#define HA_CMD_SELF_DIAG 0x2 -#define HA_CMD_READ_BUFF 0x3 -#define HA_CMD_WRITE_BUFF 0x4 - -#undef DEBUG_LINKED_COMMANDS -#undef DEBUG_DETECT -#undef DEBUG_INTERRUPT -#undef DEBUG_RESET -#undef DEBUG_GENERATE_ERRORS -#undef DEBUG_GENERATE_ABORTS -#undef DEBUG_GEOMETRY - -#define MAX_ISA 3 -#define MAX_VESA 1 -#define MAX_EISA 0 -#define MAX_PCI 0 -#define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI) -#define MAX_CHANNEL 1 -#define MAX_LUN 8 -#define MAX_TARGET 8 -#define MAX_MAILBOXES 16 -#define MAX_SGLIST 32 -#define MAX_SAFE_SGLIST 16 -#define MAX_INTERNAL_RETRIES 64 -#define MAX_CMD_PER_LUN 2 -#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN) - -#define SKIP ULONG_MAX -#define FALSE 0 -#define TRUE 1 -#define FREE 0 -#define IN_USE 1 -#define LOCKED 2 -#define IN_RESET 3 -#define IGNORE 4 -#define READY 5 -#define ABORTING 6 -#define NO_DMA 0xff -#define MAXLOOP 10000 -#define TAG_DISABLED 0 -#define TAG_SIMPLE 1 -#define TAG_ORDERED 2 - -#define REG_LCL_MASK 0 -#define REG_LCL_INTR 1 -#define REG_SYS_MASK 2 -#define REG_SYS_INTR 3 -#define REG_PRODUCT_ID1 4 -#define REG_PRODUCT_ID2 5 -#define REG_CONFIG1 6 -#define REG_CONFIG2 7 -#define REG_OGM 8 -#define REG_ICM 12 -#define REGION_SIZE 13UL -#define BSY_ASSERTED 0x01 -#define IRQ_ASSERTED 0x01 -#define CMD_RESET 0xc0 -#define CMD_OGM_INTR 0x01 -#define CMD_CLR_INTR 0x01 -#define CMD_ENA_INTR 0x81 -#define ASOK 0x00 -#define ASST 0x91 - -#define YESNO(a) ((a) ? 'y' : 'n') -#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM) - -#define PACKED __attribute__((packed)) - -struct sg_list { - unsigned int address; /* Segment Address */ - unsigned int num_bytes; /* Segment Length */ - }; - -/* MailBox SCSI Command Packet */ -struct mscp { - -#if defined(__BIG_ENDIAN_BITFIELD) - unsigned char sg:1, ca:1, dcn:1, xdir:2, opcode:3; - unsigned char lun: 3, channel:2, target:3; -#else - unsigned char opcode: 3, /* type of command */ - xdir: 2, /* data transfer direction */ - dcn: 1, /* disable disconnect */ - ca: 1, /* use cache (if available) */ - sg: 1; /* scatter/gather operation */ - unsigned char target: 3, /* SCSI target id */ - channel: 2, /* SCSI channel number */ - lun: 3; /* SCSI logical unit number */ -#endif - - unsigned int data_address PACKED; /* transfer data pointer */ - unsigned int data_len PACKED; /* length in bytes */ - unsigned int link_address PACKED; /* for linking command chains */ - unsigned char clink_id; /* identifies command in chain */ - unsigned char use_sg; /* (if sg is set) 8 bytes per list */ - unsigned char sense_len; - unsigned char cdb_len; /* 6, 10, or 12 */ - unsigned char cdb[12]; /* SCSI Command Descriptor Block */ - unsigned char adapter_status; /* non-zero indicates HA error */ - unsigned char target_status; /* non-zero indicates target error */ - unsigned int sense_addr PACKED; - - /* Additional fields begin here. */ - struct scsi_cmnd *SCpnt; - unsigned int cpp_index; /* cp index */ - - /* All the cp structure is zero filled by queuecommand except the - following CP_TAIL_SIZE bytes, initialized by detect */ - dma_addr_t cp_dma_addr; /* dma handle for this cp structure */ - struct sg_list *sglist; /* pointer to the allocated SG list */ - }; - -#define CP_TAIL_SIZE (sizeof(struct sglist *) + sizeof(dma_addr_t)) - -struct hostdata { - struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */ - unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */ - unsigned int last_cp_used; /* Index of last mailbox used */ - unsigned int iocount; /* Total i/o done for this board */ - int board_number; /* Number of this board */ - char board_name[16]; /* Name of this board */ - int in_reset; /* True if board is doing a reset */ - int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */ - int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If TRUE redo i/o on target */ - unsigned int retries; /* Number of internal retries */ - unsigned long last_retried_pid; /* Pid of last retried command */ - unsigned char subversion; /* Bus type, either ISA or ESA */ - struct pci_dev *pdev; /* Always NULL */ - unsigned char heads; - unsigned char sectors; - char board_id[256]; /* data from INQUIRY on this board */ - }; - -static struct Scsi_Host *sh[MAX_BOARDS + 1]; -static const char *driver_name = "Ux4F"; -static char sha[MAX_BOARDS]; -static DEFINE_SPINLOCK(driver_lock); - -/* Initialize num_boards so that ihdlr can work while detect is in progress */ -static unsigned int num_boards = MAX_BOARDS; - -static unsigned long io_port[] = { - - /* Space for MAX_INT_PARAM ports usable while loading as a module */ - SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, - SKIP, SKIP, - - /* Possible ISA/VESA ports */ - 0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140, - - /* End of list */ - 0x0 - }; - -#define HD(board) ((struct hostdata *) &sh[board]->hostdata) -#define BN(board) (HD(board)->board_name) - -/* Device is Little Endian */ -#define H2DEV(x) cpu_to_le32(x) -#define DEV2H(x) le32_to_cpu(x) - -static irqreturn_t do_interrupt_handler(int, void *); -static void flush_dev(struct scsi_device *, unsigned long, unsigned int, unsigned int); -static int do_trace = FALSE; -static int setup_done = FALSE; -static int link_statistics; -static int ext_tran = FALSE; - -#if defined(HAVE_OLD_UX4F_FIRMWARE) -static int have_old_firmware = TRUE; -#else -static int have_old_firmware = FALSE; -#endif - -#if defined(CONFIG_SCSI_U14_34F_TAGGED_QUEUE) -static int tag_mode = TAG_SIMPLE; -#else -static int tag_mode = TAG_DISABLED; -#endif - -#if defined(CONFIG_SCSI_U14_34F_LINKED_COMMANDS) -static int linked_comm = TRUE; -#else -static int linked_comm = FALSE; -#endif - -#if defined(CONFIG_SCSI_U14_34F_MAX_TAGS) -static int max_queue_depth = CONFIG_SCSI_U14_34F_MAX_TAGS; -#else -static int max_queue_depth = MAX_CMD_PER_LUN; -#endif - -#define MAX_INT_PARAM 10 -#define MAX_BOOT_OPTIONS_SIZE 256 -static char boot_options[MAX_BOOT_OPTIONS_SIZE]; - -#if defined(MODULE) -#include -#include - -module_param_string(u14_34f, boot_options, MAX_BOOT_OPTIONS_SIZE, 0); -MODULE_PARM_DESC(u14_34f, " equivalent to the \"u14-34f=...\" kernel boot " \ -"option." \ -" Example: modprobe u14-34f \"u14_34f=0x340,0x330,lc:y,tm:0,mq:4\""); -MODULE_AUTHOR("Dario Ballabio"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("UltraStor 14F/34F SCSI Driver"); - -#endif - -static int u14_34f_slave_configure(struct scsi_device *dev) { - int j, tqd, utqd; - char *tag_suffix, *link_suffix; - struct Scsi_Host *host = dev->host; - - j = ((struct hostdata *) host->hostdata)->board_number; - - utqd = MAX_CMD_PER_LUN; - tqd = max_queue_depth; - - if (TLDEV(dev->type) && dev->tagged_supported) - - if (tag_mode == TAG_SIMPLE) { - scsi_change_queue_depth(dev, tqd); - tag_suffix = ", simple tags"; - } - else if (tag_mode == TAG_ORDERED) { - scsi_change_queue_depth(dev, tqd); - tag_suffix = ", ordered tags"; - } - else { - scsi_change_queue_depth(dev, tqd); - tag_suffix = ", no tags"; - } - - else if (TLDEV(dev->type) && linked_comm) { - scsi_change_queue_depth(dev, tqd); - tag_suffix = ", untagged"; - } - - else { - scsi_change_queue_depth(dev, utqd); - tag_suffix = ""; - } - - if (TLDEV(dev->type) && linked_comm && dev->queue_depth > 2) - link_suffix = ", sorted"; - else if (TLDEV(dev->type)) - link_suffix = ", unsorted"; - else - link_suffix = ""; - - sdev_printk(KERN_INFO, dev, "cmds/lun %d%s%s.\n", - dev->queue_depth, link_suffix, tag_suffix); - - return FALSE; -} - -static int wait_on_busy(unsigned long iobase, unsigned int loop) { - - while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED) { - udelay(1L); - if (--loop == 0) return TRUE; - } - - return FALSE; -} - -static int board_inquiry(unsigned int j) { - struct mscp *cpp; - dma_addr_t id_dma_addr; - unsigned int limit = 0; - unsigned long time; - - id_dma_addr = pci_map_single(HD(j)->pdev, HD(j)->board_id, - sizeof(HD(j)->board_id), PCI_DMA_BIDIRECTIONAL); - cpp = &HD(j)->cp[0]; - cpp->cp_dma_addr = pci_map_single(HD(j)->pdev, cpp, sizeof(struct mscp), - PCI_DMA_BIDIRECTIONAL); - memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE); - cpp->opcode = OP_HOST_ADAPTER; - cpp->xdir = DTD_IN; - cpp->data_address = H2DEV(id_dma_addr); - cpp->data_len = H2DEV(sizeof(HD(j)->board_id)); - cpp->cdb_len = 6; - cpp->cdb[0] = HA_CMD_INQUIRY; - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - printk("%s: board_inquiry, adapter busy.\n", BN(j)); - return TRUE; - } - - HD(j)->cp_stat[0] = IGNORE; - - /* Clear the interrupt indication */ - outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); - - /* Store pointer in OGM address bytes */ - outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); - - /* Issue OGM interrupt */ - outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); - - spin_unlock_irq(&driver_lock); - time = jiffies; - while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L); - spin_lock_irq(&driver_lock); - - if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) { - HD(j)->cp_stat[0] = FREE; - printk("%s: board_inquiry, err 0x%x.\n", BN(j), cpp->adapter_status); - return TRUE; - } - - pci_unmap_single(HD(j)->pdev, cpp->cp_dma_addr, sizeof(struct mscp), - PCI_DMA_BIDIRECTIONAL); - pci_unmap_single(HD(j)->pdev, id_dma_addr, sizeof(HD(j)->board_id), - PCI_DMA_BIDIRECTIONAL); - return FALSE; -} - -static int port_detect \ - (unsigned long port_base, unsigned int j, struct scsi_host_template *tpnt) { - unsigned char irq, dma_channel, subversion, i; - unsigned char in_byte; - char *bus_type, dma_name[16]; - - /* Allowed BIOS base addresses (NULL indicates reserved) */ - unsigned long bios_segment_table[8] = { - 0, - 0xc4000, 0xc8000, 0xcc000, 0xd0000, - 0xd4000, 0xd8000, 0xdc000 - }; - - /* Allowed IRQs */ - unsigned char interrupt_table[4] = { 15, 14, 11, 10 }; - - /* Allowed DMA channels for ISA (0 indicates reserved) */ - unsigned char dma_channel_table[4] = { 5, 6, 7, 0 }; - - /* Head/sector mappings */ - struct { - unsigned char heads; - unsigned char sectors; - } mapping_table[4] = { - { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 } - }; - - struct config_1 { - -#if defined(__BIG_ENDIAN_BITFIELD) - unsigned char dma_channel: 2, interrupt:2, - removable_disks_as_fixed:1, bios_segment: 3; -#else - unsigned char bios_segment: 3, removable_disks_as_fixed: 1, - interrupt: 2, dma_channel: 2; -#endif - - } config_1; - - struct config_2 { - -#if defined(__BIG_ENDIAN_BITFIELD) - unsigned char tfr_port: 2, bios_drive_number: 1, - mapping_mode: 2, ha_scsi_id: 3; -#else - unsigned char ha_scsi_id: 3, mapping_mode: 2, - bios_drive_number: 1, tfr_port: 2; -#endif - - } config_2; - - char name[16]; - - sprintf(name, "%s%d", driver_name, j); - - if (!request_region(port_base, REGION_SIZE, driver_name)) { -#if defined(DEBUG_DETECT) - printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base); -#endif - goto fail; - } - - spin_lock_irq(&driver_lock); - - if (inb(port_base + REG_PRODUCT_ID1) != PRODUCT_ID1) goto freelock; - - in_byte = inb(port_base + REG_PRODUCT_ID2); - - if ((in_byte & 0xf0) != PRODUCT_ID2) goto freelock; - - *(char *)&config_1 = inb(port_base + REG_CONFIG1); - *(char *)&config_2 = inb(port_base + REG_CONFIG2); - - irq = interrupt_table[config_1.interrupt]; - dma_channel = dma_channel_table[config_1.dma_channel]; - subversion = (in_byte & 0x0f); - - /* Board detected, allocate its IRQ */ - if (request_irq(irq, do_interrupt_handler, - (subversion == ESA) ? IRQF_SHARED : 0, - driver_name, (void *) &sha[j])) { - printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq); - goto freelock; - } - - if (subversion == ISA && request_dma(dma_channel, driver_name)) { - printk("%s: unable to allocate DMA channel %u, detaching.\n", - name, dma_channel); - goto freeirq; - } - - if (have_old_firmware) tpnt->use_clustering = DISABLE_CLUSTERING; - - spin_unlock_irq(&driver_lock); - sh[j] = scsi_register(tpnt, sizeof(struct hostdata)); - spin_lock_irq(&driver_lock); - - if (sh[j] == NULL) { - printk("%s: unable to register host, detaching.\n", name); - goto freedma; - } - - sh[j]->io_port = port_base; - sh[j]->unique_id = port_base; - sh[j]->n_io_port = REGION_SIZE; - sh[j]->base = bios_segment_table[config_1.bios_segment]; - sh[j]->irq = irq; - sh[j]->sg_tablesize = MAX_SGLIST; - sh[j]->this_id = config_2.ha_scsi_id; - sh[j]->can_queue = MAX_MAILBOXES; - sh[j]->cmd_per_lun = MAX_CMD_PER_LUN; - -#if defined(DEBUG_DETECT) - { - unsigned char sys_mask, lcl_mask; - - sys_mask = inb(sh[j]->io_port + REG_SYS_MASK); - lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK); - printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask); - } -#endif - - /* Probably a bogus host scsi id, set it to the dummy value */ - if (sh[j]->this_id == 0) sh[j]->this_id = -1; - - /* If BIOS is disabled, force enable interrupts */ - if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK); - - memset(HD(j), 0, sizeof(struct hostdata)); - HD(j)->heads = mapping_table[config_2.mapping_mode].heads; - HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors; - HD(j)->subversion = subversion; - HD(j)->pdev = NULL; - HD(j)->board_number = j; - - if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST; - - if (HD(j)->subversion == ESA) { - sh[j]->unchecked_isa_dma = FALSE; - sh[j]->dma_channel = NO_DMA; - sprintf(BN(j), "U34F%d", j); - bus_type = "VESA"; - } - else { - unsigned long flags; - sh[j]->unchecked_isa_dma = TRUE; - - flags=claim_dma_lock(); - disable_dma(dma_channel); - clear_dma_ff(dma_channel); - set_dma_mode(dma_channel, DMA_MODE_CASCADE); - enable_dma(dma_channel); - release_dma_lock(flags); - - sh[j]->dma_channel = dma_channel; - sprintf(BN(j), "U14F%d", j); - bus_type = "ISA"; - } - - sh[j]->max_channel = MAX_CHANNEL - 1; - sh[j]->max_id = MAX_TARGET; - sh[j]->max_lun = MAX_LUN; - - if (HD(j)->subversion == ISA && !board_inquiry(j)) { - HD(j)->board_id[40] = 0; - - if (strcmp(&HD(j)->board_id[32], "06000600")) { - printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]); - printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n", - BN(j), &HD(j)->board_id[32]); - sh[j]->hostt->use_clustering = DISABLE_CLUSTERING; - sh[j]->sg_tablesize = MAX_SAFE_SGLIST; - } - } - - if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST"); - else sprintf(dma_name, "DMA %u", dma_channel); - - spin_unlock_irq(&driver_lock); - - for (i = 0; i < sh[j]->can_queue; i++) - HD(j)->cp[i].cp_dma_addr = pci_map_single(HD(j)->pdev, - &HD(j)->cp[i], sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL); - - for (i = 0; i < sh[j]->can_queue; i++) - if (! ((&HD(j)->cp[i])->sglist = kmalloc( - sh[j]->sg_tablesize * sizeof(struct sg_list), - (sh[j]->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC))) { - printk("%s: kmalloc SGlist failed, mbox %d, detaching.\n", BN(j), i); - goto release; - } - - if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN) - max_queue_depth = MAX_TAGGED_CMD_PER_LUN; - - if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN; - - if (tag_mode != TAG_DISABLED && tag_mode != TAG_SIMPLE) - tag_mode = TAG_ORDERED; - - if (j == 0) { - printk("UltraStor 14F/34F: Copyright (C) 1994-2003 Dario Ballabio.\n"); - printk("%s config options -> of:%c, tm:%d, lc:%c, mq:%d, et:%c.\n", - driver_name, YESNO(have_old_firmware), tag_mode, - YESNO(linked_comm), max_queue_depth, YESNO(ext_tran)); - } - - printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d.\n", - BN(j), bus_type, (unsigned long)sh[j]->io_port, (int)sh[j]->base, - sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue); - - if (sh[j]->max_id > 8 || sh[j]->max_lun > 8) - printk("%s: wide SCSI support enabled, max_id %u, max_lun %llu.\n", - BN(j), sh[j]->max_id, sh[j]->max_lun); - - for (i = 0; i <= sh[j]->max_channel; i++) - printk("%s: SCSI channel %u enabled, host target ID %d.\n", - BN(j), i, sh[j]->this_id); - - return TRUE; - -freedma: - if (subversion == ISA) free_dma(dma_channel); -freeirq: - free_irq(irq, &sha[j]); -freelock: - spin_unlock_irq(&driver_lock); - release_region(port_base, REGION_SIZE); -fail: - return FALSE; - -release: - u14_34f_release(sh[j]); - return FALSE; -} - -static void internal_setup(char *str, int *ints) { - int i, argc = ints[0]; - char *cur = str, *pc; - - if (argc > 0) { - - if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM; - - for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; - - io_port[i] = 0; - setup_done = TRUE; - } - - while (cur && (pc = strchr(cur, ':'))) { - int val = 0, c = *++pc; - - if (c == 'n' || c == 'N') val = FALSE; - else if (c == 'y' || c == 'Y') val = TRUE; - else val = (int) simple_strtoul(pc, NULL, 0); - - if (!strncmp(cur, "lc:", 3)) linked_comm = val; - else if (!strncmp(cur, "of:", 3)) have_old_firmware = val; - else if (!strncmp(cur, "tm:", 3)) tag_mode = val; - else if (!strncmp(cur, "tc:", 3)) tag_mode = val; - else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val; - else if (!strncmp(cur, "ls:", 3)) link_statistics = val; - else if (!strncmp(cur, "et:", 3)) ext_tran = val; - - if ((cur = strchr(cur, ','))) ++cur; - } - - return; -} - -static int option_setup(char *str) { - int ints[MAX_INT_PARAM]; - char *cur = str; - int i = 1; - - while (cur && isdigit(*cur) && i < MAX_INT_PARAM) { - ints[i++] = simple_strtoul(cur, NULL, 0); - - if ((cur = strchr(cur, ',')) != NULL) cur++; - } - - ints[0] = i - 1; - internal_setup(cur, ints); - return 1; -} - -static int u14_34f_detect(struct scsi_host_template *tpnt) { - unsigned int j = 0, k; - - tpnt->proc_name = "u14-34f"; - - if(strlen(boot_options)) option_setup(boot_options); - -#if defined(MODULE) - /* io_port could have been modified when loading as a module */ - if(io_port[0] != SKIP) { - setup_done = TRUE; - io_port[MAX_INT_PARAM] = 0; - } -#endif - - for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL; - - for (k = 0; io_port[k]; k++) { - - if (io_port[k] == SKIP) continue; - - if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt)) j++; - } - - num_boards = j; - return j; -} - -static void map_dma(unsigned int i, unsigned int j) { - unsigned int data_len = 0; - unsigned int k, pci_dir; - int count; - struct scatterlist *sg; - struct mscp *cpp; - struct scsi_cmnd *SCpnt; - - cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; - pci_dir = SCpnt->sc_data_direction; - - if (SCpnt->sense_buffer) - cpp->sense_addr = H2DEV(pci_map_single(HD(j)->pdev, SCpnt->sense_buffer, - SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE)); - - cpp->sense_len = SCSI_SENSE_BUFFERSIZE; - - if (scsi_bufflen(SCpnt)) { - count = scsi_dma_map(SCpnt); - BUG_ON(count < 0); - - scsi_for_each_sg(SCpnt, sg, count, k) { - cpp->sglist[k].address = H2DEV(sg_dma_address(sg)); - cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(sg)); - data_len += sg->length; - } - - cpp->sg = TRUE; - cpp->use_sg = scsi_sg_count(SCpnt); - cpp->data_address = - H2DEV(pci_map_single(HD(j)->pdev, cpp->sglist, - cpp->use_sg * sizeof(struct sg_list), - pci_dir)); - cpp->data_len = H2DEV(data_len); - - } else { - pci_dir = PCI_DMA_BIDIRECTIONAL; - cpp->data_len = H2DEV(scsi_bufflen(SCpnt)); - } -} - -static void unmap_dma(unsigned int i, unsigned int j) { - unsigned int pci_dir; - struct mscp *cpp; - struct scsi_cmnd *SCpnt; - - cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; - pci_dir = SCpnt->sc_data_direction; - - if (DEV2H(cpp->sense_addr)) - pci_unmap_single(HD(j)->pdev, DEV2H(cpp->sense_addr), - DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); - - scsi_dma_unmap(SCpnt); - - if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; - - if (DEV2H(cpp->data_address)) - pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address), - DEV2H(cpp->data_len), pci_dir); -} - -static void sync_dma(unsigned int i, unsigned int j) { - unsigned int pci_dir; - struct mscp *cpp; - struct scsi_cmnd *SCpnt; - - cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; - pci_dir = SCpnt->sc_data_direction; - - if (DEV2H(cpp->sense_addr)) - pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->sense_addr), - DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); - - if (scsi_sg_count(SCpnt)) - pci_dma_sync_sg_for_cpu(HD(j)->pdev, scsi_sglist(SCpnt), - scsi_sg_count(SCpnt), pci_dir); - - if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; - - if (DEV2H(cpp->data_address)) - pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->data_address), - DEV2H(cpp->data_len), pci_dir); -} - -static void scsi_to_dev_dir(unsigned int i, unsigned int j) { - unsigned int k; - - static const unsigned char data_out_cmds[] = { - 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e, - 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40, - 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b, 0x5d - }; - - static const unsigned char data_none_cmds[] = { - 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e, - 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47, - 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5, 0x00 - }; - - struct mscp *cpp; - struct scsi_cmnd *SCpnt; - - cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; - - if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) { - cpp->xdir = DTD_IN; - return; - } - else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) { - cpp->xdir = DTD_OUT; - return; - } - else if (SCpnt->sc_data_direction == DMA_NONE) { - cpp->xdir = DTD_NONE; - return; - } - - if (SCpnt->sc_data_direction != DMA_BIDIRECTIONAL) - panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n", BN(j)); - - cpp->xdir = DTD_IN; - - for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++) - if (SCpnt->cmnd[0] == data_out_cmds[k]) { - cpp->xdir = DTD_OUT; - break; - } - - if (cpp->xdir == DTD_IN) - for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++) - if (SCpnt->cmnd[0] == data_none_cmds[k]) { - cpp->xdir = DTD_NONE; - break; - } - -} - -static int u14_34f_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) { - unsigned int i, j, k; - struct mscp *cpp; - - /* j is the board number */ - j = ((struct hostdata *) SCpnt->device->host->hostdata)->board_number; - - if (SCpnt->host_scribble) - panic("%s: qcomm, SCpnt %p already active.\n", - BN(j), SCpnt); - - /* i is the mailbox number, look for the first free mailbox - starting from last_cp_used */ - i = HD(j)->last_cp_used + 1; - - for (k = 0; k < sh[j]->can_queue; k++, i++) { - - if (i >= sh[j]->can_queue) i = 0; - - if (HD(j)->cp_stat[i] == FREE) { - HD(j)->last_cp_used = i; - break; - } - } - - if (k == sh[j]->can_queue) { - printk("%s: qcomm, no free mailbox.\n", BN(j)); - return 1; - } - - /* Set pointer to control packet structure */ - cpp = &HD(j)->cp[i]; - - memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE); - SCpnt->scsi_done = done; - cpp->cpp_index = i; - SCpnt->host_scribble = (unsigned char *) &cpp->cpp_index; - - if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%u.\n", - BN(j), i, SCpnt->device->channel, SCpnt->device->id, - (u8)SCpnt->device->lun); - - cpp->opcode = OP_SCSI; - cpp->channel = SCpnt->device->channel; - cpp->target = SCpnt->device->id; - cpp->lun = (u8)SCpnt->device->lun; - cpp->SCpnt = SCpnt; - cpp->cdb_len = SCpnt->cmd_len; - memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len); - - /* Use data transfer direction SCpnt->sc_data_direction */ - scsi_to_dev_dir(i, j); - - /* Map DMA buffers and SG list */ - map_dma(i, j); - - if (linked_comm && SCpnt->device->queue_depth > 2 - && TLDEV(SCpnt->device->type)) { - HD(j)->cp_stat[i] = READY; - flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), j, FALSE); - return 0; - } - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - unmap_dma(i, j); - SCpnt->host_scribble = NULL; - scmd_printk(KERN_INFO, SCpnt, - "qcomm, adapter busy.\n"); - return 1; - } - - /* Store pointer in OGM address bytes */ - outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); - - /* Issue OGM interrupt */ - outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); - - HD(j)->cp_stat[i] = IN_USE; - return 0; -} - -static DEF_SCSI_QCMD(u14_34f_queuecommand) - -static int u14_34f_eh_abort(struct scsi_cmnd *SCarg) { - unsigned int i, j; - - j = ((struct hostdata *) SCarg->device->host->hostdata)->board_number; - - if (SCarg->host_scribble == NULL) { - scmd_printk(KERN_INFO, SCarg, "abort, command inactive.\n"); - return SUCCESS; - } - - i = *(unsigned int *)SCarg->host_scribble; - scmd_printk(KERN_INFO, SCarg, "abort, mbox %d.\n", i); - - if (i >= sh[j]->can_queue) - panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j)); - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - printk("%s: abort, timeout error.\n", BN(j)); - return FAILED; - } - - if (HD(j)->cp_stat[i] == FREE) { - printk("%s: abort, mbox %d is free.\n", BN(j), i); - return SUCCESS; - } - - if (HD(j)->cp_stat[i] == IN_USE) { - printk("%s: abort, mbox %d is in use.\n", BN(j), i); - - if (SCarg != HD(j)->cp[i].SCpnt) - panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n", - BN(j), i, SCarg, HD(j)->cp[i].SCpnt); - - if (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED) - printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i); - - return FAILED; - } - - if (HD(j)->cp_stat[i] == IN_RESET) { - printk("%s: abort, mbox %d is in reset.\n", BN(j), i); - return FAILED; - } - - if (HD(j)->cp_stat[i] == LOCKED) { - printk("%s: abort, mbox %d is locked.\n", BN(j), i); - return SUCCESS; - } - - if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) { - unmap_dma(i, j); - SCarg->result = DID_ABORT << 16; - SCarg->host_scribble = NULL; - HD(j)->cp_stat[i] = FREE; - printk("%s, abort, mbox %d ready, DID_ABORT, done.\n", BN(j), i); - SCarg->scsi_done(SCarg); - return SUCCESS; - } - - panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i); -} - -static int u14_34f_eh_host_reset(struct scsi_cmnd *SCarg) { - unsigned int i, j, k, c, limit = 0; - unsigned long time; - int arg_done = FALSE; - struct scsi_cmnd *SCpnt; - - j = ((struct hostdata *) SCarg->device->host->hostdata)->board_number; - scmd_printk(KERN_INFO, SCarg, "reset, enter.\n"); - - spin_lock_irq(sh[j]->host_lock); - - if (SCarg->host_scribble == NULL) - printk("%s: reset, inactive.\n", BN(j)); - - if (HD(j)->in_reset) { - printk("%s: reset, exit, already in reset.\n", BN(j)); - spin_unlock_irq(sh[j]->host_lock); - return FAILED; - } - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - printk("%s: reset, exit, timeout error.\n", BN(j)); - spin_unlock_irq(sh[j]->host_lock); - return FAILED; - } - - HD(j)->retries = 0; - - for (c = 0; c <= sh[j]->max_channel; c++) - for (k = 0; k < sh[j]->max_id; k++) { - HD(j)->target_redo[k][c] = TRUE; - HD(j)->target_to[k][c] = 0; - } - - for (i = 0; i < sh[j]->can_queue; i++) { - - if (HD(j)->cp_stat[i] == FREE) continue; - - if (HD(j)->cp_stat[i] == LOCKED) { - HD(j)->cp_stat[i] = FREE; - printk("%s: reset, locked mbox %d forced free.\n", BN(j), i); - continue; - } - - if (!(SCpnt = HD(j)->cp[i].SCpnt)) - panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i); - - if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) { - HD(j)->cp_stat[i] = ABORTING; - printk("%s: reset, mbox %d aborting.\n", BN(j), i); - } - - else { - HD(j)->cp_stat[i] = IN_RESET; - printk("%s: reset, mbox %d in reset.\n", BN(j), i); - } - - if (SCpnt->host_scribble == NULL) - panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i); - - if (*(unsigned int *)SCpnt->host_scribble != i) - panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i); - - if (SCpnt->scsi_done == NULL) - panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i); - - if (SCpnt == SCarg) arg_done = TRUE; - } - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - printk("%s: reset, cannot reset, timeout error.\n", BN(j)); - spin_unlock_irq(sh[j]->host_lock); - return FAILED; - } - - outb(CMD_RESET, sh[j]->io_port + REG_LCL_INTR); - printk("%s: reset, board reset done, enabling interrupts.\n", BN(j)); - -#if defined(DEBUG_RESET) - do_trace = TRUE; -#endif - - HD(j)->in_reset = TRUE; - - spin_unlock_irq(sh[j]->host_lock); - time = jiffies; - while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L); - spin_lock_irq(sh[j]->host_lock); - - printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit); - - for (i = 0; i < sh[j]->can_queue; i++) { - - if (HD(j)->cp_stat[i] == IN_RESET) { - SCpnt = HD(j)->cp[i].SCpnt; - unmap_dma(i, j); - SCpnt->result = DID_RESET << 16; - SCpnt->host_scribble = NULL; - - /* This mailbox is still waiting for its interrupt */ - HD(j)->cp_stat[i] = LOCKED; - - printk("%s, reset, mbox %d locked, DID_RESET, done.\n", BN(j), i); - } - - else if (HD(j)->cp_stat[i] == ABORTING) { - SCpnt = HD(j)->cp[i].SCpnt; - unmap_dma(i, j); - SCpnt->result = DID_RESET << 16; - SCpnt->host_scribble = NULL; - - /* This mailbox was never queued to the adapter */ - HD(j)->cp_stat[i] = FREE; - - printk("%s, reset, mbox %d aborting, DID_RESET, done.\n", BN(j), i); - } - - else - - /* Any other mailbox has already been set free by interrupt */ - continue; - - SCpnt->scsi_done(SCpnt); - } - - HD(j)->in_reset = FALSE; - do_trace = FALSE; - - if (arg_done) printk("%s: reset, exit, done.\n", BN(j)); - else printk("%s: reset, exit.\n", BN(j)); - - spin_unlock_irq(sh[j]->host_lock); - return SUCCESS; -} - -static int u14_34f_bios_param(struct scsi_device *disk, - struct block_device *bdev, sector_t capacity, int *dkinfo) { - unsigned int j = 0; - unsigned int size = capacity; - - dkinfo[0] = HD(j)->heads; - dkinfo[1] = HD(j)->sectors; - dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors); - - if (ext_tran && (scsicam_bios_param(bdev, capacity, dkinfo) < 0)) { - dkinfo[0] = 255; - dkinfo[1] = 63; - dkinfo[2] = size / (dkinfo[0] * dkinfo[1]); - } - -#if defined (DEBUG_GEOMETRY) - printk ("%s: bios_param, head=%d, sec=%d, cyl=%d.\n", driver_name, - dkinfo[0], dkinfo[1], dkinfo[2]); -#endif - - return FALSE; -} - -static void sort(unsigned long sk[], unsigned int da[], unsigned int n, - unsigned int rev) { - unsigned int i, j, k, y; - unsigned long x; - - for (i = 0; i < n - 1; i++) { - k = i; - - for (j = k + 1; j < n; j++) - if (rev) { - if (sk[j] > sk[k]) k = j; - } - else { - if (sk[j] < sk[k]) k = j; - } - - if (k != i) { - x = sk[k]; sk[k] = sk[i]; sk[i] = x; - y = da[k]; da[k] = da[i]; da[i] = y; - } - } - - return; - } - -static int reorder(unsigned int j, unsigned long cursec, - unsigned int ihdlr, unsigned int il[], unsigned int n_ready) { - struct scsi_cmnd *SCpnt; - struct mscp *cpp; - unsigned int k, n; - unsigned int rev = FALSE, s = TRUE, r = TRUE; - unsigned int input_only = TRUE, overlap = FALSE; - unsigned long sl[n_ready], pl[n_ready], ll[n_ready]; - unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0; - unsigned long ioseek = 0; - - static unsigned int flushcount = 0, batchcount = 0, sortcount = 0; - static unsigned int readycount = 0, ovlcount = 0, inputcount = 0; - static unsigned int readysorted = 0, revcount = 0; - static unsigned long seeksorted = 0, seeknosort = 0; - - if (link_statistics && !(++flushcount % link_statistics)) - printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\ - " av %ldK as %ldK.\n", flushcount, batchcount, inputcount, - ovlcount, readycount, readysorted, sortcount, revcount, - seeknosort / (readycount + 1), - seeksorted / (readycount + 1)); - - if (n_ready <= 1) return FALSE; - - for (n = 0; n < n_ready; n++) { - k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - - if (!(cpp->xdir == DTD_IN)) input_only = FALSE; - - if (blk_rq_pos(SCpnt->request) < minsec) - minsec = blk_rq_pos(SCpnt->request); - if (blk_rq_pos(SCpnt->request) > maxsec) - maxsec = blk_rq_pos(SCpnt->request); - - sl[n] = blk_rq_pos(SCpnt->request); - ioseek += blk_rq_sectors(SCpnt->request); - - if (!n) continue; - - if (sl[n] < sl[n - 1]) s = FALSE; - if (sl[n] > sl[n - 1]) r = FALSE; - - if (link_statistics) { - if (sl[n] > sl[n - 1]) - seek += sl[n] - sl[n - 1]; - else - seek += sl[n - 1] - sl[n]; - } - - } - - if (link_statistics) { - if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec; - } - - if (cursec > ((maxsec + minsec) / 2)) rev = TRUE; - - if (ioseek > ((maxsec - minsec) / 2)) rev = FALSE; - - if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev); - - if (!input_only) for (n = 0; n < n_ready; n++) { - k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - ll[n] = blk_rq_sectors(SCpnt->request); pl[n] = SCpnt->serial_number; - - if (!n) continue; - - if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n])) - || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE; - } - - if (overlap) sort(pl, il, n_ready, FALSE); - - if (link_statistics) { - if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec; - batchcount++; readycount += n_ready; seeknosort += seek / 1024; - if (input_only) inputcount++; - if (overlap) { ovlcount++; seeksorted += iseek / 1024; } - else seeksorted += (iseek + maxsec - minsec) / 1024; - if (rev && !r) { revcount++; readysorted += n_ready; } - if (!rev && !s) { sortcount++; readysorted += n_ready; } - } - -#if defined(DEBUG_LINKED_COMMANDS) - if (link_statistics && (overlap || !(flushcount % link_statistics))) - for (n = 0; n < n_ready; n++) { - k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - printk("%s %d.%d:%llu mb %d fc %d nr %d sec %ld ns %u"\ - " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n", - (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target, - (u8)SCpnt->lun, k, flushcount, n_ready, - blk_rq_pos(SCpnt->request), blk_rq_sectors(SCpnt->request), - cursec, YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only), - YESNO(overlap), cpp->xdir); - } -#endif - return overlap; -} - -static void flush_dev(struct scsi_device *dev, unsigned long cursec, unsigned int j, - unsigned int ihdlr) { - struct scsi_cmnd *SCpnt; - struct mscp *cpp; - unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES]; - - for (k = 0; k < sh[j]->can_queue; k++) { - - if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue; - - cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - - if (SCpnt->device != dev) continue; - - if (HD(j)->cp_stat[k] == IN_USE) return; - - il[n_ready++] = k; - } - - if (reorder(j, cursec, ihdlr, il, n_ready)) n_ready = 1; - - for (n = 0; n < n_ready; n++) { - k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - scmd_printk(KERN_INFO, SCpnt, - "%s, mbox %d, adapter" - " busy, will abort.\n", (ihdlr ? "ihdlr" : "qcomm"), - k); - HD(j)->cp_stat[k] = ABORTING; - continue; - } - - outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); - outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); - HD(j)->cp_stat[k] = IN_USE; - } - -} - -static irqreturn_t ihdlr(unsigned int j) -{ - struct scsi_cmnd *SCpnt; - unsigned int i, k, c, status, tstatus, reg, ret; - struct mscp *spp, *cpp; - int irq = sh[j]->irq; - - /* Check if this board need to be serviced */ - if (!((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED)) goto none; - - HD(j)->iocount++; - - if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq, - HD(j)->iocount); - - /* Check if this board is still busy */ - if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) { - outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); - printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n", - BN(j), irq, reg, HD(j)->iocount); - goto none; - } - - ret = inl(sh[j]->io_port + REG_ICM); - - /* Clear interrupt pending flag */ - outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); - - /* Find the mailbox to be serviced on this board */ - for (i = 0; i < sh[j]->can_queue; i++) - if (H2DEV(HD(j)->cp[i].cp_dma_addr) == ret) break; - - if (i >= sh[j]->can_queue) - panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j), - (void *)ret, (void *)H2DEV(HD(j)->cp[0].cp_dma_addr)); - - cpp = &(HD(j)->cp[i]); - spp = cpp; - -#if defined(DEBUG_GENERATE_ABORTS) - if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 500) < 3)) goto handled; -#endif - - if (HD(j)->cp_stat[i] == IGNORE) { - HD(j)->cp_stat[i] = FREE; - goto handled; - } - else if (HD(j)->cp_stat[i] == LOCKED) { - HD(j)->cp_stat[i] = FREE; - printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i, - HD(j)->iocount); - goto handled; - } - else if (HD(j)->cp_stat[i] == FREE) { - printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i, - HD(j)->iocount); - goto handled; - } - else if (HD(j)->cp_stat[i] == IN_RESET) - printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i); - else if (HD(j)->cp_stat[i] != IN_USE) - panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n", - BN(j), i, HD(j)->cp_stat[i]); - - HD(j)->cp_stat[i] = FREE; - SCpnt = cpp->SCpnt; - - if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i); - - if (SCpnt->host_scribble == NULL) - panic("%s: ihdlr, mbox %d, SCpnt %p garbled.\n", BN(j), i, - SCpnt); - - if (*(unsigned int *)SCpnt->host_scribble != i) - panic("%s: ihdlr, mbox %d, index mismatch %d.\n", - BN(j), i, *(unsigned int *)SCpnt->host_scribble); - - sync_dma(i, j); - - if (linked_comm && SCpnt->device->queue_depth > 2 - && TLDEV(SCpnt->device->type)) - flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), j, TRUE); - - tstatus = status_byte(spp->target_status); - -#if defined(DEBUG_GENERATE_ERRORS) - if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 200) < 2)) - spp->adapter_status = 0x01; -#endif - - switch (spp->adapter_status) { - case ASOK: /* status OK */ - - /* Forces a reset if a disk drive keeps returning BUSY */ - if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE) - status = DID_ERROR << 16; - - /* If there was a bus reset, redo operation on each target */ - else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK - && HD(j)->target_redo[scmd_id(SCpnt)][scmd_channel(SCpnt)]) - status = DID_BUS_BUSY << 16; - - /* Works around a flaw in scsi.c */ - else if (tstatus == CHECK_CONDITION - && SCpnt->device->type == TYPE_DISK - && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR) - status = DID_BUS_BUSY << 16; - - else - status = DID_OK << 16; - - if (tstatus == GOOD) - HD(j)->target_redo[scmd_id(SCpnt)][scmd_channel(SCpnt)] = FALSE; - - if (spp->target_status && SCpnt->device->type == TYPE_DISK && - (!(tstatus == CHECK_CONDITION && HD(j)->iocount <= 1000 && - (SCpnt->sense_buffer[2] & 0xf) == NOT_READY))) - scmd_printk(KERN_INFO, SCpnt, - "ihdlr, target_status 0x%x, sense key 0x%x.\n", - spp->target_status, - SCpnt->sense_buffer[2]); - - HD(j)->target_to[scmd_id(SCpnt)][scmd_channel(SCpnt)] = 0; - - if (HD(j)->last_retried_pid == SCpnt->serial_number) HD(j)->retries = 0; - - break; - case ASST: /* Selection Time Out */ - - if (HD(j)->target_to[scmd_id(SCpnt)][scmd_channel(SCpnt)] > 1) - status = DID_ERROR << 16; - else { - status = DID_TIME_OUT << 16; - HD(j)->target_to[scmd_id(SCpnt)][scmd_channel(SCpnt)]++; - } - - break; - - /* Perform a limited number of internal retries */ - case 0x93: /* Unexpected bus free */ - case 0x94: /* Target bus phase sequence failure */ - case 0x96: /* Illegal SCSI command */ - case 0xa3: /* SCSI bus reset error */ - - for (c = 0; c <= sh[j]->max_channel; c++) - for (k = 0; k < sh[j]->max_id; k++) - HD(j)->target_redo[k][c] = TRUE; - - - case 0x92: /* Data over/under-run */ - - if (SCpnt->device->type != TYPE_TAPE - && HD(j)->retries < MAX_INTERNAL_RETRIES) { - -#if defined(DID_SOFT_ERROR) - status = DID_SOFT_ERROR << 16; -#else - status = DID_BUS_BUSY << 16; -#endif - - HD(j)->retries++; - HD(j)->last_retried_pid = SCpnt->serial_number; - } - else - status = DID_ERROR << 16; - - break; - case 0x01: /* Invalid command */ - case 0x02: /* Invalid parameters */ - case 0x03: /* Invalid data list */ - case 0x84: /* SCSI bus abort error */ - case 0x9b: /* Auto request sense error */ - case 0x9f: /* Unexpected command complete message error */ - case 0xff: /* Invalid parameter in the S/G list */ - default: - status = DID_ERROR << 16; - break; - } - - SCpnt->result = status | spp->target_status; - -#if defined(DEBUG_INTERRUPT) - if (SCpnt->result || do_trace) -#else - if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) || - (spp->adapter_status != ASOK && - spp->adapter_status != ASST && HD(j)->iocount <= 1000) || - do_trace || msg_byte(spp->target_status)) -#endif - scmd_printk(KERN_INFO, SCpnt, "ihdlr, mbox %2d, err 0x%x:%x,"\ - " reg 0x%x, count %d.\n", - i, spp->adapter_status, spp->target_status, - reg, HD(j)->iocount); - - unmap_dma(i, j); - - /* Set the command state to inactive */ - SCpnt->host_scribble = NULL; - - SCpnt->scsi_done(SCpnt); - - if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq, - HD(j)->iocount); - -handled: - return IRQ_HANDLED; -none: - return IRQ_NONE; -} - -static irqreturn_t do_interrupt_handler(int irq, void *shap) { - unsigned int j; - unsigned long spin_flags; - irqreturn_t ret; - - /* Check if the interrupt must be processed by this handler */ - if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return IRQ_NONE; - - spin_lock_irqsave(sh[j]->host_lock, spin_flags); - ret = ihdlr(j); - spin_unlock_irqrestore(sh[j]->host_lock, spin_flags); - return ret; -} - -static int u14_34f_release(struct Scsi_Host *shpnt) { - unsigned int i, j; - - for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++); - - if (sh[j] == NULL) - panic("%s: release, invalid Scsi_Host pointer.\n", driver_name); - - for (i = 0; i < sh[j]->can_queue; i++) - kfree((&HD(j)->cp[i])->sglist); - - for (i = 0; i < sh[j]->can_queue; i++) - pci_unmap_single(HD(j)->pdev, HD(j)->cp[i].cp_dma_addr, - sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL); - - free_irq(sh[j]->irq, &sha[j]); - - if (sh[j]->dma_channel != NO_DMA) - free_dma(sh[j]->dma_channel); - - release_region(sh[j]->io_port, sh[j]->n_io_port); - scsi_unregister(sh[j]); - return FALSE; -} - -#include "scsi_module.c" - -#ifndef MODULE -__setup("u14-34f=", option_setup); -#endif /* end MODULE */ From 4931a46aef5c0d21422d1344fa8ec9206934e93c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 19 Sep 2016 08:50:51 -0700 Subject: [PATCH 167/173] scsi: pas16: remove from tree The driver has not seen any maintainer activity or other work that wasn't tree wide conversion or clenaups in the entire history of the git tree. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinicke Acked-by: Finn Thain Signed-off-by: Martin K. Petersen --- Documentation/scsi/scsi-parameters.txt | 3 - MAINTAINERS | 1 - drivers/scsi/Kconfig | 14 - drivers/scsi/Makefile | 1 - drivers/scsi/pas16.c | 565 ------------------------- drivers/scsi/pas16.h | 121 ------ 6 files changed, 705 deletions(-) delete mode 100644 drivers/scsi/pas16.c delete mode 100644 drivers/scsi/pas16.h diff --git a/Documentation/scsi/scsi-parameters.txt b/Documentation/scsi/scsi-parameters.txt index 083bd934218a..fe77b5a2c926 100644 --- a/Documentation/scsi/scsi-parameters.txt +++ b/Documentation/scsi/scsi-parameters.txt @@ -80,9 +80,6 @@ parameters may be changed at runtime by the command Format: , See also Documentation/scsi/st.txt. - pas16= [HW,SCSI] - See header of drivers/scsi/pas16.c. - scsi_debug_*= [SCSI] See drivers/scsi/scsi_debug.c. diff --git a/MAINTAINERS b/MAINTAINERS index 6ca763fe0346..2a0c0567082f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8052,7 +8052,6 @@ F: drivers/scsi/dtc.* F: drivers/scsi/g_NCR5380.* F: drivers/scsi/g_NCR5380_mmio.c F: drivers/scsi/mac_scsi.* -F: drivers/scsi/pas16.* F: drivers/scsi/sun3_scsi.* F: drivers/scsi/sun3_scsi_vme.c F: drivers/scsi/t128.* diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 69785315eca3..ee83d95bc3d6 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1225,20 +1225,6 @@ config SCSI_NCR53C8XX_NO_DISCONNECT not allow targets to disconnect is not reasonable if there is more than 1 device on a SCSI bus. The normal answer therefore is N. -config SCSI_PAS16 - tristate "PAS16 SCSI support" - depends on ISA && SCSI - select SCSI_SPI_ATTRS - ---help--- - This is support for a SCSI host adapter. It is explained in section - 3.10 of the SCSI-HOWTO, available from - . If it doesn't work out - of the box, you may have to change some settings in - . - - To compile this driver as a module, choose M here: the - module will be called pas16. - config SCSI_QLOGIC_FAS tristate "Qlogic FAS SCSI support" depends on ISA && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 921cf07e9b7b..be3108c0e476 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -87,7 +87,6 @@ obj-$(CONFIG_SCSI_QLA_ISCSI) += libiscsi.o qla4xxx/ obj-$(CONFIG_SCSI_LPFC) += lpfc/ obj-$(CONFIG_SCSI_BFA_FC) += bfa/ obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/ -obj-$(CONFIG_SCSI_PAS16) += pas16.o obj-$(CONFIG_SCSI_T128) += t128.o obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o obj-$(CONFIG_SCSI_HPSA) += hpsa.o diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c deleted file mode 100644 index 2f689ae7a803..000000000000 --- a/drivers/scsi/pas16.c +++ /dev/null @@ -1,565 +0,0 @@ -/* - * This driver adapted from Drew Eckhardt's Trantor T128 driver - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 666-5836 - * - * ( Based on T128 - DISTRIBUTION RELEASE 3. ) - * - * Modified to work with the Pro Audio Spectrum/Studio 16 - * by John Weidman. - * - * - * For more information, please consult - * - * Media Vision - * (510) 770-8600 - * (800) 348-7116 - */ - -/* - * The card is detected and initialized in one of several ways : - * 1. Autoprobe (default) - There are many different models of - * the Pro Audio Spectrum/Studio 16, and I only have one of - * them, so this may require a little tweaking. An interrupt - * is triggered to autoprobe for the interrupt line. Note: - * with the newer model boards, the interrupt is set via - * software after reset using the default_irq for the - * current board number. - * - * 2. With command line overrides - pas16=port,irq may be - * used on the LILO command line to override the defaults. - * - * 3. With the PAS16_OVERRIDE compile time define. This is - * specified as an array of address, irq tuples. Ie, for - * one board at the default 0x388 address, IRQ10, I could say - * -DPAS16_OVERRIDE={{0x388, 10}} - * NOTE: Untested. - * - * 4. When included as a module, with arguments passed on the command line: - * pas16_irq=xx the interrupt - * pas16_addr=xx the port - * e.g. "modprobe pas16 pas16_addr=0x388 pas16_irq=5" - * - * Note that if the override methods are used, place holders must - * be specified for other boards in the system. - * - * - * Configuration notes : - * The current driver does not support interrupt sharing with the - * sound portion of the card. If you use the same irq for the - * scsi port and sound you will have problems. Either use - * a different irq for the scsi port or don't use interrupts - * for the scsi port. - * - * If you have problems with your card not being recognized, use - * the LILO command line override. Try to get it recognized without - * interrupts. Ie, for a board at the default 0x388 base port, - * boot: linux pas16=0x388,0 - * - * NO_IRQ (0) should be specified for no interrupt, - * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden - * on the command line. - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include "pas16.h" -#include "NCR5380.h" - - -static unsigned short pas16_addr; -static int pas16_irq; - - -static const int scsi_irq_translate[] = - { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 }; - -/* The default_irqs array contains values used to set the irq into the - * board via software (as must be done on newer model boards without - * irq jumpers on the board). The first value in the array will be - * assigned to logical board 0, the next to board 1, etc. - */ -static int default_irqs[] __initdata = - { PAS16_DEFAULT_BOARD_1_IRQ, - PAS16_DEFAULT_BOARD_2_IRQ, - PAS16_DEFAULT_BOARD_3_IRQ, - PAS16_DEFAULT_BOARD_4_IRQ - }; - -static struct override { - unsigned short io_port; - int irq; -} overrides -#ifdef PAS16_OVERRIDE - [] __initdata = PAS16_OVERRIDE; -#else - [4] __initdata = {{0,IRQ_AUTO}, {0,IRQ_AUTO}, {0,IRQ_AUTO}, - {0,IRQ_AUTO}}; -#endif - -#define NO_OVERRIDES ARRAY_SIZE(overrides) - -static struct base { - unsigned short io_port; - int noauto; -} bases[] __initdata = - { {PAS16_DEFAULT_BASE_1, 0}, - {PAS16_DEFAULT_BASE_2, 0}, - {PAS16_DEFAULT_BASE_3, 0}, - {PAS16_DEFAULT_BASE_4, 0} - }; - -#define NO_BASES ARRAY_SIZE(bases) - -static const unsigned short pas16_offset[ 8 ] = - { - 0x1c00, /* OUTPUT_DATA_REG */ - 0x1c01, /* INITIATOR_COMMAND_REG */ - 0x1c02, /* MODE_REG */ - 0x1c03, /* TARGET_COMMAND_REG */ - 0x3c00, /* STATUS_REG ro, SELECT_ENABLE_REG wo */ - 0x3c01, /* BUS_AND_STATUS_REG ro, START_DMA_SEND_REG wo */ - 0x3c02, /* INPUT_DATA_REGISTER ro, (N/A on PAS16 ?) - * START_DMA_TARGET_RECEIVE_REG wo - */ - 0x3c03, /* RESET_PARITY_INTERRUPT_REG ro, - * START_DMA_INITIATOR_RECEIVE_REG wo - */ - }; - - -/* - * Function : enable_board( int board_num, unsigned short port ) - * - * Purpose : set address in new model board - * - * Inputs : board_num - logical board number 0-3, port - base address - * - */ - -static void __init - enable_board( int board_num, unsigned short port ) -{ - outb( 0xbc + board_num, MASTER_ADDRESS_PTR ); - outb( port >> 2, MASTER_ADDRESS_PTR ); -} - - - -/* - * Function : init_board( unsigned short port, int irq ) - * - * Purpose : Set the board up to handle the SCSI interface - * - * Inputs : port - base address of the board, - * irq - irq to assign to the SCSI port - * force_irq - set it even if it conflicts with sound driver - * - */ - -static void __init - init_board( unsigned short io_port, int irq, int force_irq ) -{ - unsigned int tmp; - unsigned int pas_irq_code; - - /* Initialize the SCSI part of the board */ - - outb( 0x30, io_port + P_TIMEOUT_COUNTER_REG ); /* Timeout counter */ - outb( 0x01, io_port + P_TIMEOUT_STATUS_REG_OFFSET ); /* Reset TC */ - outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */ - - inb(io_port + pas16_offset[RESET_PARITY_INTERRUPT_REG]); - - /* Set the SCSI interrupt pointer without mucking up the sound - * interrupt pointer in the same byte. - */ - pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0; - tmp = inb( io_port + IO_CONFIG_3 ); - - if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0 - && !force_irq ) - { - printk( "pas16: WARNING: Can't use same irq as sound " - "driver -- interrupts disabled\n" ); - /* Set up the drive parameters, disable 5380 interrupts */ - outb( 0x4d, io_port + SYS_CONFIG_4 ); - } - else - { - tmp = ( tmp & 0x0f ) | ( pas_irq_code << 4 ); - outb( tmp, io_port + IO_CONFIG_3 ); - - /* Set up the drive parameters and enable 5380 interrupts */ - outb( 0x6d, io_port + SYS_CONFIG_4 ); - } -} - - -/* - * Function : pas16_hw_detect( unsigned short board_num ) - * - * Purpose : determine if a pas16 board is present - * - * Inputs : board_num - logical board number ( 0 - 3 ) - * - * Returns : 0 if board not found, 1 if found. - */ - -static int __init - pas16_hw_detect( unsigned short board_num ) -{ - unsigned char board_rev, tmp; - unsigned short io_port = bases[ board_num ].io_port; - - /* See if we can find a PAS16 board at the address associated - * with this logical board number. - */ - - /* First, attempt to take a newer model board out of reset and - * give it a base address. This shouldn't affect older boards. - */ - enable_board( board_num, io_port ); - - /* Now see if it looks like a PAS16 board */ - board_rev = inb( io_port + PCB_CONFIG ); - - if( board_rev == 0xff ) - return 0; - - tmp = board_rev ^ 0xe0; - - outb( tmp, io_port + PCB_CONFIG ); - tmp = inb( io_port + PCB_CONFIG ); - outb( board_rev, io_port + PCB_CONFIG ); - - if( board_rev != tmp ) /* Not a PAS-16 */ - return 0; - - if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 ) - return 0; /* return if no SCSI interface found */ - - /* Mediavision has some new model boards that return ID bits - * that indicate a SCSI interface, but they're not (LMS). We'll - * put in an additional test to try to weed them out. - */ - - outb(0x01, io_port + WAIT_STATE); /* 1 Wait state */ - outb(0x20, io_port + pas16_offset[MODE_REG]); /* Is it really SCSI? */ - if (inb(io_port + pas16_offset[MODE_REG]) != 0x20) /* Write to a reg. */ - return 0; /* and try to read */ - outb(0x00, io_port + pas16_offset[MODE_REG]); /* it back. */ - if (inb(io_port + pas16_offset[MODE_REG]) != 0x00) - return 0; - - return 1; -} - - -#ifndef MODULE -/* - * Function : pas16_setup(char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - */ - -static int __init pas16_setup(char *str) -{ - static int commandline_current; - int i; - int ints[10]; - - get_options(str, ARRAY_SIZE(ints), ints); - if (ints[0] != 2) - printk("pas16_setup : usage pas16=io_port,irq\n"); - else - if (commandline_current < NO_OVERRIDES) { - overrides[commandline_current].io_port = (unsigned short) ints[1]; - overrides[commandline_current].irq = ints[2]; - for (i = 0; i < NO_BASES; ++i) - if (bases[i].io_port == (unsigned short) ints[1]) { - bases[i].noauto = 1; - break; - } - ++commandline_current; - } - return 1; -} - -__setup("pas16=", pas16_setup); -#endif - -/* - * Function : int pas16_detect(struct scsi_host_template * tpnt) - * - * Purpose : detects and initializes PAS16 controllers - * that were autoprobed, overridden on the LILO command line, - * or specified at compile time. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * - */ - -static int __init pas16_detect(struct scsi_host_template *tpnt) -{ - static int current_override; - static unsigned short current_base; - struct Scsi_Host *instance; - unsigned short io_port; - int count; - - if (pas16_addr != 0) { - overrides[0].io_port = pas16_addr; - /* - * This is how we avoid seeing more than - * one host adapter at the same I/O port. - * Cribbed shamelessly from pas16_setup(). - */ - for (count = 0; count < NO_BASES; ++count) - if (bases[count].io_port == pas16_addr) { - bases[count].noauto = 1; - break; - } - } - if (pas16_irq != 0) - overrides[0].irq = pas16_irq; - - for (count = 0; current_override < NO_OVERRIDES; ++current_override) { - io_port = 0; - - if (overrides[current_override].io_port) - { - io_port = overrides[current_override].io_port; - enable_board( current_override, io_port ); - init_board( io_port, overrides[current_override].irq, 1 ); - } - else - for (; !io_port && (current_base < NO_BASES); ++current_base) { - dprintk(NDEBUG_INIT, "pas16: probing io_port 0x%04x\n", - (unsigned int)bases[current_base].io_port); - if ( !bases[current_base].noauto && - pas16_hw_detect( current_base ) ){ - io_port = bases[current_base].io_port; - init_board( io_port, default_irqs[ current_base ], 0 ); - dprintk(NDEBUG_INIT, "pas16: detected board\n"); - } - } - - dprintk(NDEBUG_INIT, "pas16: io_port = 0x%04x\n", - (unsigned int)io_port); - - if (!io_port) - break; - - instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); - if(instance == NULL) - goto out; - - instance->io_port = io_port; - - if (NCR5380_init(instance, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP)) - goto out_unregister; - - NCR5380_maybe_reset_bus(instance); - - if (overrides[current_override].irq != IRQ_AUTO) - instance->irq = overrides[current_override].irq; - else - instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS); - - /* Compatibility with documented NCR5380 kernel parameters */ - if (instance->irq == 255) - instance->irq = NO_IRQ; - - if (instance->irq != NO_IRQ) - if (request_irq(instance->irq, pas16_intr, 0, - "pas16", instance)) { - printk("scsi%d : IRQ%d not free, interrupts disabled\n", - instance->host_no, instance->irq); - instance->irq = NO_IRQ; - } - - if (instance->irq == NO_IRQ) { - printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); - printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); - /* Disable 5380 interrupts, leave drive params the same */ - outb( 0x4d, io_port + SYS_CONFIG_4 ); - outb( (inb(io_port + IO_CONFIG_3) & 0x0f), io_port + IO_CONFIG_3 ); - } - - dprintk(NDEBUG_INIT, "scsi%d : irq = %d\n", - instance->host_no, instance->irq); - - ++current_override; - ++count; - } - return count; - -out_unregister: - scsi_unregister(instance); -out: - return count; -} - -/* - * Function : int pas16_biosparam(Disk *disk, struct block_device *dev, int *ip) - * - * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for - * the specified device / size. - * - * Inputs : size = size of device in sectors (512 bytes), dev = block device - * major / minor, ip[] = {heads, sectors, cylinders} - * - * Returns : always 0 (success), initializes ip - * - */ - -/* - * XXX Most SCSI boards use this mapping, I could be incorrect. Some one - * using hard disks on a trantor should verify that this mapping corresponds - * to that used by the BIOS / ASPI driver by running the linux fdisk program - * and matching the H_C_S coordinates to what DOS uses. - */ - -static int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev, - sector_t capacity, int *ip) -{ - int size = capacity; - ip[0] = 64; - ip[1] = 32; - ip[2] = size >> 11; /* I think I have it as /(32*64) */ - if( ip[2] > 1024 ) { /* yes, >, not >= */ - ip[0]=255; - ip[1]=63; - ip[2]=size/(63*255); - if( ip[2] > 1023 ) /* yes >1023... */ - ip[2] = 1023; - } - - return 0; -} - -/* - * Function : int pas16_pread (struct Scsi_Host *instance, - * unsigned char *dst, int len) - * - * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to - * dst - * - * Inputs : dst = destination, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. - */ - -static inline int pas16_pread(struct Scsi_Host *instance, - unsigned char *dst, int len) -{ - register unsigned char *d = dst; - register unsigned short reg = (unsigned short) (instance->io_port + - P_DATA_REG_OFFSET); - register int i = len; - int ii = 0; - - while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) ) - ++ii; - - insb( reg, d, i ); - - if ( inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) { - outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET); - printk("scsi%d : watchdog timer fired in NCR5380_pread()\n", - instance->host_no); - return -1; - } - return 0; -} - -/* - * Function : int pas16_pwrite (struct Scsi_Host *instance, - * unsigned char *src, int len) - * - * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from - * src - * - * Inputs : src = source, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. - */ - -static inline int pas16_pwrite(struct Scsi_Host *instance, - unsigned char *src, int len) -{ - register unsigned char *s = src; - register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET); - register int i = len; - int ii = 0; - - while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) ) - ++ii; - - outsb( reg, s, i ); - - if (inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) { - outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET); - printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n", - instance->host_no); - return -1; - } - return 0; -} - -#include "NCR5380.c" - -static int pas16_release(struct Scsi_Host *shost) -{ - if (shost->irq != NO_IRQ) - free_irq(shost->irq, shost); - NCR5380_exit(shost); - scsi_unregister(shost); - return 0; -} - -static struct scsi_host_template driver_template = { - .name = "Pro Audio Spectrum-16 SCSI", - .detect = pas16_detect, - .release = pas16_release, - .proc_name = "pas16", - .info = pas16_info, - .queuecommand = pas16_queue_command, - .eh_abort_handler = pas16_abort, - .eh_bus_reset_handler = pas16_bus_reset, - .bios_param = pas16_biosparam, - .can_queue = 32, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING, - .cmd_size = NCR5380_CMD_SIZE, - .max_sectors = 128, -}; -#include "scsi_module.c" - -#ifdef MODULE -module_param(pas16_addr, ushort, 0); -module_param(pas16_irq, int, 0); -#endif -MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/pas16.h b/drivers/scsi/pas16.h deleted file mode 100644 index 9fe7f33660b4..000000000000 --- a/drivers/scsi/pas16.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This driver adapted from Drew Eckhardt's Trantor T128 driver - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 666-5836 - * - * ( Based on T128 - DISTRIBUTION RELEASE 3. ) - * - * Modified to work with the Pro Audio Spectrum/Studio 16 - * by John Weidman. - * - * - * For more information, please consult - * - * Media Vision - * (510) 770-8600 - * (800) 348-7116 - */ - - -#ifndef PAS16_H -#define PAS16_H - -#define PAS16_DEFAULT_BASE_1 0x388 -#define PAS16_DEFAULT_BASE_2 0x384 -#define PAS16_DEFAULT_BASE_3 0x38c -#define PAS16_DEFAULT_BASE_4 0x288 - -#define PAS16_DEFAULT_BOARD_1_IRQ 10 -#define PAS16_DEFAULT_BOARD_2_IRQ 12 -#define PAS16_DEFAULT_BOARD_3_IRQ 14 -#define PAS16_DEFAULT_BOARD_4_IRQ 15 - - -/* - * The Pro Audio Spectrum boards are I/O mapped. They use a Zilog 5380 - * SCSI controller, which is the equivalent of NCR's 5380. "Pseudo-DMA" - * architecture is used, where a PAL drives the DMA signals on the 5380 - * allowing fast, blind transfers with proper handshaking. - */ - - -/* The Time-out Counter register is used to safe-guard against a stuck - * bus (in the case of RDY driven handshake) or a stuck byte (if 16-Bit - * DMA conversion is used). The counter uses a 28.224MHz clock - * divided by 14 as its clock source. In the case of a stuck byte in - * the holding register, an interrupt is generated (and mixed with the - * one with the drive) using the CD-ROM interrupt pointer. - */ - -#define P_TIMEOUT_COUNTER_REG 0x4000 -#define P_TC_DISABLE 0x80 /* Set to 0 to enable timeout int. */ - /* Bits D6-D0 contain timeout count */ - - -#define P_TIMEOUT_STATUS_REG_OFFSET 0x4001 -#define P_TS_TIM 0x80 /* check timeout status */ - /* Bits D6-D4 N/U */ -#define P_TS_ARM_DRQ_INT 0x08 /* Arm DRQ Int. When set high, - * the next rising edge will - * cause a CD-ROM interrupt. - * When set low, the interrupt - * will be cleared. There is - * no status available for - * this interrupt. - */ -#define P_TS_ENABLE_TO_ERR_INTERRUPT /* Enable timeout error int. */ -#define P_TS_ENABLE_WAIT /* Enable Wait */ - -#define P_TS_CT 0x01 /* clear timeout. Note: writing - * to this register clears the - * timeout error int. or status - */ - - -/* - * The data register reads/writes to/from the 5380 in pseudo-DMA mode - */ - -#define P_DATA_REG_OFFSET 0x5c00 /* rw */ - -#define P_STATUS_REG_OFFSET 0x5c01 /* ro */ -#define P_ST_RDY 0x80 /* 5380 DDRQ Status */ - -#define P_IRQ_STATUS 0x5c03 -#define P_IS_IRQ 0x80 /* DIRQ status */ - -#define PCB_CONFIG 0x803 -#define MASTER_ADDRESS_PTR 0x9a01 /* Fixed position - no relo */ -#define SYS_CONFIG_4 0x8003 -#define WAIT_STATE 0xbc00 -#define OPERATION_MODE_1 0xec03 -#define IO_CONFIG_3 0xf002 - -#define NCR5380_implementation_fields /* none */ - -#define PAS16_io_port(reg) (instance->io_port + pas16_offset[(reg)]) - -#define NCR5380_read(reg) ( inb(PAS16_io_port(reg)) ) -#define NCR5380_write(reg, value) ( outb((value),PAS16_io_port(reg)) ) - -#define NCR5380_dma_xfer_len(instance, cmd, phase) (cmd->transfersize) -#define NCR5380_dma_recv_setup pas16_pread -#define NCR5380_dma_send_setup pas16_pwrite -#define NCR5380_dma_residual(instance) (0) - -#define NCR5380_intr pas16_intr -#define NCR5380_queue_command pas16_queue_command -#define NCR5380_abort pas16_abort -#define NCR5380_bus_reset pas16_bus_reset -#define NCR5380_info pas16_info - -/* 15 14 12 10 7 5 3 - 1101 0100 1010 1000 */ - -#define PAS16_IRQS 0xd4a8 - -#endif /* PAS16_H */ From f95819a09c2d6ff2e8a784ff46ba8836f937d07b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 19 Sep 2016 08:50:52 -0700 Subject: [PATCH 168/173] scsi: t128: remove from tree The driver has not seen any maintainer activity or other work that wasn't tree wide conversion or clenaups in the entire history of the git tree. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinicke Acked-by: Finn Thain Signed-off-by: Martin K. Petersen --- Documentation/scsi/scsi-parameters.txt | 3 - MAINTAINERS | 1 - drivers/scsi/Kconfig | 17 -- drivers/scsi/Makefile | 1 - drivers/scsi/t128.c | 407 ------------------------- drivers/scsi/t128.h | 97 ------ 6 files changed, 526 deletions(-) delete mode 100644 drivers/scsi/t128.c delete mode 100644 drivers/scsi/t128.h diff --git a/Documentation/scsi/scsi-parameters.txt b/Documentation/scsi/scsi-parameters.txt index fe77b5a2c926..00a03c0cd8c2 100644 --- a/Documentation/scsi/scsi-parameters.txt +++ b/Documentation/scsi/scsi-parameters.txt @@ -113,9 +113,6 @@ parameters may be changed at runtime by the command sym53c416= [HW,SCSI] See header of drivers/scsi/sym53c416.c. - t128= [HW,SCSI] - See header of drivers/scsi/t128.c. - tmscsim= [HW,SCSI] See comment before function dc390_setup() in drivers/scsi/tmscsim.c. diff --git a/MAINTAINERS b/MAINTAINERS index 2a0c0567082f..e4ec1b8abbb7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8054,7 +8054,6 @@ F: drivers/scsi/g_NCR5380_mmio.c F: drivers/scsi/mac_scsi.* F: drivers/scsi/sun3_scsi.* F: drivers/scsi/sun3_scsi_vme.c -F: drivers/scsi/t128.* NCR DUAL 700 SCSI DRIVER (MICROCHANNEL) M: "James E.J. Bottomley" diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index ee83d95bc3d6..a38e37cd722c 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1345,23 +1345,6 @@ config SCSI_AM53C974 To compile this driver as a module, choose M here: the module will be called am53c974. -config SCSI_T128 - tristate "Trantor T128/T128F/T228 SCSI support" - depends on ISA && SCSI - select SCSI_SPI_ATTRS - select CHECK_SIGNATURE - ---help--- - This is support for a SCSI host adapter. It is explained in section - 3.11 of the SCSI-HOWTO, available from - . If it doesn't work out - of the box, you may have to change some settings in - . Note that Trantor was purchased by - Adaptec, and some former Trantor products are being sold under the - Adaptec name. - - To compile this driver as a module, choose M here: the - module will be called t128. - config SCSI_NSP32 tristate "Workbit NinjaSCSI-32Bi/UDE support" depends on PCI && SCSI && !64BIT diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index be3108c0e476..392609a1190a 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -87,7 +87,6 @@ obj-$(CONFIG_SCSI_QLA_ISCSI) += libiscsi.o qla4xxx/ obj-$(CONFIG_SCSI_LPFC) += lpfc/ obj-$(CONFIG_SCSI_BFA_FC) += bfa/ obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/ -obj-$(CONFIG_SCSI_T128) += t128.o obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o obj-$(CONFIG_SCSI_HPSA) += hpsa.o obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi/ diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c deleted file mode 100644 index 8a8608ac62e6..000000000000 --- a/drivers/scsi/t128.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Trantor T128/T128F/T228 driver - * Note : architecturally, the T100 and T130 are different and won't - * work - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - * - * For more information, please consult - * - * Trantor Systems, Ltd. - * T128/T128F/T228 SCSI Host Adapter - * Hardware Specifications - * - * Trantor Systems, Ltd. - * 5415 Randall Place - * Fremont, CA 94538 - * 1+ (415) 770-1400, FAX 1+ (415) 770-9910 - */ - -/* - * The card is detected and initialized in one of several ways : - * 1. Autoprobe (default) - since the board is memory mapped, - * a BIOS signature is scanned for to locate the registers. - * An interrupt is triggered to autoprobe for the interrupt - * line. - * - * 2. With command line overrides - t128=address,irq may be - * used on the LILO command line to override the defaults. - * - * 3. With the T128_OVERRIDE compile time define. This is - * specified as an array of address, irq tuples. Ie, for - * one board at the default 0xcc000 address, IRQ5, I could say - * -DT128_OVERRIDE={{0xcc000, 5}} - * - * Note that if the override methods are used, place holders must - * be specified for other boards in the system. - * - * T128/T128F jumper/dipswitch settings (note : on my sample, the switches - * were epoxy'd shut, meaning I couldn't change the 0xcc000 base address) : - * - * T128 Sw7 Sw8 Sw6 = 0ws Sw5 = boot - * T128F Sw6 Sw7 Sw5 = 0ws Sw4 = boot Sw8 = floppy disable - * cc000 off off - * c8000 off on - * dc000 on off - * d8000 on on - * - * - * Interrupts - * There is a 12 pin jumper block, jp1, numbered as follows : - * T128 (JP1) T128F (J5) - * 2 4 6 8 10 12 11 9 7 5 3 1 - * 1 3 5 7 9 11 12 10 8 6 4 2 - * - * 3 2-4 - * 5 1-3 - * 7 3-5 - * T128F only - * 10 8-10 - * 12 7-9 - * 14 10-12 - * 15 9-11 - */ - -#include -#include -#include -#include -#include - -#include -#include "t128.h" -#include "NCR5380.h" - -static struct override { - unsigned long address; - int irq; -} overrides -#ifdef T128_OVERRIDE - [] __initdata = T128_OVERRIDE; -#else - [4] __initdata = {{0, IRQ_AUTO}, {0, IRQ_AUTO}, - {0 ,IRQ_AUTO}, {0, IRQ_AUTO}}; -#endif - -#define NO_OVERRIDES ARRAY_SIZE(overrides) - -static struct base { - unsigned int address; - int noauto; -} bases[] __initdata = { - { 0xcc000, 0}, { 0xc8000, 0}, { 0xdc000, 0}, { 0xd8000, 0} -}; - -#define NO_BASES ARRAY_SIZE(bases) - -static struct signature { - const char *string; - int offset; -} signatures[] __initdata = { -{"TSROM: SCSI BIOS, Version 1.12", 0x36}, -}; - -#define NO_SIGNATURES ARRAY_SIZE(signatures) - -#ifndef MODULE -/* - * Function : t128_setup(char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - */ - -static int __init t128_setup(char *str) -{ - static int commandline_current; - int i; - int ints[10]; - - get_options(str, ARRAY_SIZE(ints), ints); - if (ints[0] != 2) - printk("t128_setup : usage t128=address,irq\n"); - else - if (commandline_current < NO_OVERRIDES) { - overrides[commandline_current].address = ints[1]; - overrides[commandline_current].irq = ints[2]; - for (i = 0; i < NO_BASES; ++i) - if (bases[i].address == ints[1]) { - bases[i].noauto = 1; - break; - } - ++commandline_current; - } - return 1; -} - -__setup("t128=", t128_setup); -#endif - -/* - * Function : int t128_detect(struct scsi_host_template * tpnt) - * - * Purpose : detects and initializes T128,T128F, or T228 controllers - * that were autoprobed, overridden on the LILO command line, - * or specified at compile time. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * - */ - -static int __init t128_detect(struct scsi_host_template *tpnt) -{ - static int current_override, current_base; - struct Scsi_Host *instance; - unsigned long base; - void __iomem *p; - int sig, count; - - for (count = 0; current_override < NO_OVERRIDES; ++current_override) { - base = 0; - p = NULL; - - if (overrides[current_override].address) { - base = overrides[current_override].address; - p = ioremap(bases[current_base].address, 0x2000); - if (!p) - base = 0; - } else - for (; !base && (current_base < NO_BASES); ++current_base) { - dprintk(NDEBUG_INIT, "t128: probing address 0x%08x\n", - bases[current_base].address); - if (bases[current_base].noauto) - continue; - p = ioremap(bases[current_base].address, 0x2000); - if (!p) - continue; - for (sig = 0; sig < NO_SIGNATURES; ++sig) - if (check_signature(p + signatures[sig].offset, - signatures[sig].string, - strlen(signatures[sig].string))) { - base = bases[current_base].address; - dprintk(NDEBUG_INIT, "t128: detected board\n"); - goto found; - } - iounmap(p); - } - - dprintk(NDEBUG_INIT, "t128: base = 0x%08x\n", (unsigned int)base); - - if (!base) - break; - -found: - instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); - if(instance == NULL) - goto out_unmap; - - instance->base = base; - ((struct NCR5380_hostdata *)instance->hostdata)->base = p; - - if (NCR5380_init(instance, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP)) - goto out_unregister; - - NCR5380_maybe_reset_bus(instance); - - if (overrides[current_override].irq != IRQ_AUTO) - instance->irq = overrides[current_override].irq; - else - instance->irq = NCR5380_probe_irq(instance, T128_IRQS); - - /* Compatibility with documented NCR5380 kernel parameters */ - if (instance->irq == 255) - instance->irq = NO_IRQ; - - if (instance->irq != NO_IRQ) - if (request_irq(instance->irq, t128_intr, 0, "t128", - instance)) { - printk("scsi%d : IRQ%d not free, interrupts disabled\n", - instance->host_no, instance->irq); - instance->irq = NO_IRQ; - } - - if (instance->irq == NO_IRQ) { - printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); - printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); - } - - dprintk(NDEBUG_INIT, "scsi%d: irq = %d\n", - instance->host_no, instance->irq); - - ++current_override; - ++count; - } - return count; - -out_unregister: - scsi_unregister(instance); -out_unmap: - iounmap(p); - return count; -} - -static int t128_release(struct Scsi_Host *shost) -{ - struct NCR5380_hostdata *hostdata = shost_priv(shost); - - if (shost->irq != NO_IRQ) - free_irq(shost->irq, shost); - NCR5380_exit(shost); - scsi_unregister(shost); - iounmap(hostdata->base); - return 0; -} - -/* - * Function : int t128_biosparam(Disk * disk, struct block_device *dev, int *ip) - * - * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for - * the specified device / size. - * - * Inputs : size = size of device in sectors (512 bytes), dev = block device - * major / minor, ip[] = {heads, sectors, cylinders} - * - * Returns : always 0 (success), initializes ip - * - */ - -/* - * XXX Most SCSI boards use this mapping, I could be incorrect. Some one - * using hard disks on a trantor should verify that this mapping corresponds - * to that used by the BIOS / ASPI driver by running the linux fdisk program - * and matching the H_C_S coordinates to what DOS uses. - */ - -static int t128_biosparam(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int *ip) -{ - ip[0] = 64; - ip[1] = 32; - ip[2] = capacity >> 11; - return 0; -} - -/* - * Function : int t128_pread (struct Scsi_Host *instance, - * unsigned char *dst, int len) - * - * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to - * dst - * - * Inputs : dst = destination, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. - */ - -static inline int t128_pread(struct Scsi_Host *instance, - unsigned char *dst, int len) -{ - struct NCR5380_hostdata *hostdata = shost_priv(instance); - void __iomem *reg, *base = hostdata->base; - unsigned char *d = dst; - register int i = len; - - reg = base + T_DATA_REG_OFFSET; - -#if 0 - for (; i; --i) { - while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier(); -#else - while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier(); - for (; i; --i) { -#endif - *d++ = readb(reg); - } - - if (readb(base + T_STATUS_REG_OFFSET) & T_ST_TIM) { - unsigned char tmp; - void __iomem *foo = base + T_CONTROL_REG_OFFSET; - tmp = readb(foo); - writeb(tmp | T_CR_CT, foo); - writeb(tmp, foo); - printk("scsi%d : watchdog timer fired in NCR5380_pread()\n", - instance->host_no); - return -1; - } else - return 0; -} - -/* - * Function : int t128_pwrite (struct Scsi_Host *instance, - * unsigned char *src, int len) - * - * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from - * src - * - * Inputs : src = source, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. - */ - -static inline int t128_pwrite(struct Scsi_Host *instance, - unsigned char *src, int len) -{ - struct NCR5380_hostdata *hostdata = shost_priv(instance); - void __iomem *reg, *base = hostdata->base; - unsigned char *s = src; - register int i = len; - - reg = base + T_DATA_REG_OFFSET; - -#if 0 - for (; i; --i) { - while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier(); -#else - while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier(); - for (; i; --i) { -#endif - writeb(*s++, reg); - } - - if (readb(base + T_STATUS_REG_OFFSET) & T_ST_TIM) { - unsigned char tmp; - void __iomem *foo = base + T_CONTROL_REG_OFFSET; - tmp = readb(foo); - writeb(tmp | T_CR_CT, foo); - writeb(tmp, foo); - printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n", - instance->host_no); - return -1; - } else - return 0; -} - -MODULE_LICENSE("GPL"); - -#include "NCR5380.c" - -static struct scsi_host_template driver_template = { - .name = "Trantor T128/T128F/T228", - .detect = t128_detect, - .release = t128_release, - .proc_name = "t128", - .info = t128_info, - .queuecommand = t128_queue_command, - .eh_abort_handler = t128_abort, - .eh_bus_reset_handler = t128_bus_reset, - .bios_param = t128_biosparam, - .can_queue = 32, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING, - .cmd_size = NCR5380_CMD_SIZE, - .max_sectors = 128, -}; -#include "scsi_module.c" diff --git a/drivers/scsi/t128.h b/drivers/scsi/t128.h deleted file mode 100644 index c95bcd839109..000000000000 --- a/drivers/scsi/t128.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Trantor T128/T128F/T228 defines - * Note : architecturally, the T100 and T128 are different and won't work - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - * - * For more information, please consult - * - * Trantor Systems, Ltd. - * T128/T128F/T228 SCSI Host Adapter - * Hardware Specifications - * - * Trantor Systems, Ltd. - * 5415 Randall Place - * Fremont, CA 94538 - * 1+ (415) 770-1400, FAX 1+ (415) 770-9910 - */ - -#ifndef T128_H -#define T128_H - -/* - * The trantor boards are memory mapped. They use an NCR5380 or - * equivalent (my sample board had part second sourced from ZILOG). - * NCR's recommended "Pseudo-DMA" architecture is used, where - * a PAL drives the DMA signals on the 5380 allowing fast, blind - * transfers with proper handshaking. - */ - -/* - * Note : a boot switch is provided for the purpose of informing the - * firmware to boot or not boot from attached SCSI devices. So, I imagine - * there are fewer people who've yanked the ROM like they do on the Seagate - * to make bootup faster, and I'll probably use this for autodetection. - */ -#define T_ROM_OFFSET 0 - -/* - * Note : my sample board *WAS NOT* populated with the SRAM, so this - * can't be used for autodetection without a ROM present. - */ -#define T_RAM_OFFSET 0x1800 - -/* - * All of the registers are allocated 32 bytes of address space, except - * for the data register (read/write to/from the 5380 in pseudo-DMA mode) - */ -#define T_CONTROL_REG_OFFSET 0x1c00 /* rw */ -#define T_CR_INT 0x10 /* Enable interrupts */ -#define T_CR_CT 0x02 /* Reset watchdog timer */ - -#define T_STATUS_REG_OFFSET 0x1c20 /* ro */ -#define T_ST_BOOT 0x80 /* Boot switch */ -#define T_ST_S3 0x40 /* User settable switches, */ -#define T_ST_S2 0x20 /* read 0 when switch is on, 1 off */ -#define T_ST_S1 0x10 -#define T_ST_PS2 0x08 /* Set for Microchannel 228 */ -#define T_ST_RDY 0x04 /* 5380 DRQ */ -#define T_ST_TIM 0x02 /* indicates 40us watchdog timer fired */ -#define T_ST_ZERO 0x01 /* Always zero */ - -#define T_5380_OFFSET 0x1d00 /* 8 registers here, see NCR5380.h */ - -#define T_DATA_REG_OFFSET 0x1e00 /* rw 512 bytes long */ - -#define NCR5380_implementation_fields \ - void __iomem *base - -#define T128_address(reg) \ - (((struct NCR5380_hostdata *)shost_priv(instance))->base + T_5380_OFFSET + ((reg) * 0x20)) - -#define NCR5380_read(reg) readb(T128_address(reg)) -#define NCR5380_write(reg, value) writeb((value),(T128_address(reg))) - -#define NCR5380_dma_xfer_len(instance, cmd, phase) (cmd->transfersize) -#define NCR5380_dma_recv_setup t128_pread -#define NCR5380_dma_send_setup t128_pwrite -#define NCR5380_dma_residual(instance) (0) - -#define NCR5380_intr t128_intr -#define NCR5380_queue_command t128_queue_command -#define NCR5380_abort t128_abort -#define NCR5380_bus_reset t128_bus_reset -#define NCR5380_info t128_info - -#define NCR5380_io_delay(x) udelay(x) - -/* 15 14 12 10 7 5 3 - 1101 0100 1010 1000 */ - -#define T128_IRQS 0xc4a8 - -#endif /* T128_H */ From 92efbb8500230c883ca6966b0ef9f3e18c1e29ba Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 19 Sep 2016 08:50:53 -0700 Subject: [PATCH 169/173] scsi: dtc: remove from tree The driver has not seen any maintainer activity or other work that wasn't tree wide conversion or clenaups in the entire history of the git tree. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinicke Acked-by: Finn Thain Signed-off-by: Martin K. Petersen --- Documentation/scsi/dtc3x80.txt | 43 --- Documentation/scsi/scsi-parameters.txt | 3 - MAINTAINERS | 2 - drivers/scsi/Kconfig | 14 - drivers/scsi/Makefile | 1 - drivers/scsi/dtc.c | 447 ------------------------- drivers/scsi/dtc.h | 42 --- 7 files changed, 552 deletions(-) delete mode 100644 Documentation/scsi/dtc3x80.txt delete mode 100644 drivers/scsi/dtc.c delete mode 100644 drivers/scsi/dtc.h diff --git a/Documentation/scsi/dtc3x80.txt b/Documentation/scsi/dtc3x80.txt deleted file mode 100644 index 1d7af9f9a8ed..000000000000 --- a/Documentation/scsi/dtc3x80.txt +++ /dev/null @@ -1,43 +0,0 @@ -README file for the Linux DTC3180/3280 scsi driver. -by Ray Van Tassle (rayvt@comm.mot.com) March 1996 -Based on the generic & core NCR5380 code by Drew Eckhard - -SCSI device driver for the DTC 3180/3280. -Data Technology Corp---a division of Qume. - -The 3280 has a standard floppy interface. - -The 3180 does not. Otherwise, they are identical. - -The DTC3x80 does not support DMA but it does have Pseudo-DMA which is -supported by the driver. - -Its DTC406 scsi chip is supposedly compatible with the NCR 53C400. -It is memory mapped, uses an IRQ, but no dma or io-port. There is -internal DMA, between SCSI bus and an on-chip 128-byte buffer. Double -buffering is done automagically by the chip. Data is transferred -between the on-chip buffer and CPU/RAM via memory moves. - -The driver detects the possible memory addresses (jumper selectable): - CC00, DC00, C800, and D800 -The possible IRQ's (jumper selectable) are: - IRQ 10, 11, 12, 15 -Parity is supported by the chip, but not by this driver. -Information can be obtained from /proc/scsi/dtc3c80/N. - -Note on interrupts: - -The documentation says that it can be set to interrupt whenever the -on-chip buffer needs CPU attention. I couldn't get this to work. So -the driver polls for data-ready in the pseudo-DMA transfer routine. -The interrupt support routines in the NCR3280.c core modules handle -scsi disconnect/reconnect, and this (mostly) works. However..... I -have tested it with 4 totally different hard drives (both SCSI-1 and -SCSI-2), and one CDROM drive. Interrupts works great for all but one -specific hard drive. For this one, the driver will eventually hang in -the transfer state. I have tested with: "dd bs=4k count=2k -of=/dev/null if=/dev/sdb". It reads ok for a while, then hangs. -After beating my head against this for a couple of weeks, getting -nowhere, I give up. So.....This driver does NOT use interrupts, even -if you have the card jumpered to an IRQ. Probably nobody will ever -care. diff --git a/Documentation/scsi/scsi-parameters.txt b/Documentation/scsi/scsi-parameters.txt index 00a03c0cd8c2..8e66dafa41e1 100644 --- a/Documentation/scsi/scsi-parameters.txt +++ b/Documentation/scsi/scsi-parameters.txt @@ -34,9 +34,6 @@ parameters may be changed at runtime by the command See drivers/scsi/BusLogic.c, comment before function BusLogic_ParseDriverOptions(). - dtc3181e= [HW,SCSI] - See Documentation/scsi/g_NCR5380.txt. - eata= [HW,SCSI] fdomain= [HW,SCSI] diff --git a/MAINTAINERS b/MAINTAINERS index e4ec1b8abbb7..48f2b480ac6e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8042,13 +8042,11 @@ M: Michael Schmitz L: linux-scsi@vger.kernel.org S: Maintained F: Documentation/scsi/g_NCR5380.txt -F: Documentation/scsi/dtc3x80.txt F: drivers/scsi/NCR5380.* F: drivers/scsi/arm/cumana_1.c F: drivers/scsi/arm/oak.c F: drivers/scsi/atari_scsi.* F: drivers/scsi/dmx3191d.c -F: drivers/scsi/dtc.* F: drivers/scsi/g_NCR5380.* F: drivers/scsi/g_NCR5380_mmio.c F: drivers/scsi/mac_scsi.* diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index a38e37cd722c..3e2bdb90813c 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -637,20 +637,6 @@ config SCSI_DMX3191D To compile this driver as a module, choose M here: the module will be called dmx3191d. -config SCSI_DTC3280 - tristate "DTC3180/3280 SCSI support" - depends on ISA && SCSI - select SCSI_SPI_ATTRS - select CHECK_SIGNATURE - help - This is support for DTC 3180/3280 SCSI Host Adapters. Please read - the SCSI-HOWTO, available from - , and the file - . - - To compile this driver as a module, choose M here: the - module will be called dtc. - config SCSI_EATA tristate "EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support" depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 392609a1190a..38d938d7fe67 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -90,7 +90,6 @@ obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/ obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o obj-$(CONFIG_SCSI_HPSA) += hpsa.o obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi/ -obj-$(CONFIG_SCSI_DTC3280) += dtc.o obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/ obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c deleted file mode 100644 index 459863f94e46..000000000000 --- a/drivers/scsi/dtc.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * DTC 3180/3280 driver, by - * Ray Van Tassle rayvt@comm.mot.com - * - * taken from ... - * Trantor T128/T128F/T228 driver by... - * - * Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - */ - -/* - * The card is detected and initialized in one of several ways : - * 1. Autoprobe (default) - since the board is memory mapped, - * a BIOS signature is scanned for to locate the registers. - * An interrupt is triggered to autoprobe for the interrupt - * line. - * - * 2. With command line overrides - dtc=address,irq may be - * used on the LILO command line to override the defaults. - * -*/ - -/*----------------------------------------------------------------*/ -/* the following will set the monitor border color (useful to find - where something crashed or gets stuck at */ -/* 1 = blue - 2 = green - 3 = cyan - 4 = red - 5 = magenta - 6 = yellow - 7 = white -*/ -#if 0 -#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);} -#else -#define rtrc(i) {} -#endif - - -#include -#include -#include -#include -#include -#include -#include - -#include "dtc.h" -#include "NCR5380.h" - -/* - * The DTC3180 & 3280 boards are memory mapped. - * - */ - -/* - */ -/* Offset from DTC_5380_OFFSET */ -#define DTC_CONTROL_REG 0x100 /* rw */ -#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */ -#define CSR_DIR_READ 0x40 /* rw direction, 1 = read 0 = write */ - -#define CSR_RESET 0x80 /* wo Resets 53c400 */ -#define CSR_5380_REG 0x80 /* ro 5380 registers can be accessed */ -#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */ -#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */ -#define CSR_5380_INTR 0x10 /* rw Enable 5380 interrupts */ -#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */ -#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Host buffer not ready */ -#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer ready */ -#define CSR_GATED_5380_IRQ 0x01 /* ro Last block xferred */ -#define CSR_INT_BASE (CSR_SCSI_BUFF_INTR | CSR_5380_INTR) - - -#define DTC_BLK_CNT 0x101 /* rw - * # of 128-byte blocks to transfer */ - - -#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */ - -#define DTC_SWITCH_REG 0x3982 /* ro - DIP switches */ -#define DTC_RESUME_XFER 0x3982 /* wo - resume data xfer - * after disconnect/reconnect*/ - -#define DTC_5380_OFFSET 0x3880 /* 8 registers here, see NCR5380.h */ - -/*!!!! for dtc, it's a 128 byte buffer at 3900 !!! */ -#define DTC_DATA_BUF 0x3900 /* rw 128 bytes long */ - -static struct override { - unsigned int address; - int irq; -} overrides -#ifdef OVERRIDE -[] __initdata = OVERRIDE; -#else -[4] __initdata = { - { 0, IRQ_AUTO }, { 0, IRQ_AUTO }, { 0, IRQ_AUTO }, { 0, IRQ_AUTO } -}; -#endif - -#define NO_OVERRIDES ARRAY_SIZE(overrides) - -static struct base { - unsigned long address; - int noauto; -} bases[] __initdata = { - { 0xcc000, 0 }, - { 0xc8000, 0 }, - { 0xdc000, 0 }, - { 0xd8000, 0 } -}; - -#define NO_BASES ARRAY_SIZE(bases) - -static const struct signature { - const char *string; - int offset; -} signatures[] = { - {"DATA TECHNOLOGY CORPORATION BIOS", 0x25}, -}; - -#define NO_SIGNATURES ARRAY_SIZE(signatures) - -#ifndef MODULE -/* - * Function : dtc_setup(char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - */ - -static int __init dtc_setup(char *str) -{ - static int commandline_current; - int i; - int ints[10]; - - get_options(str, ARRAY_SIZE(ints), ints); - if (ints[0] != 2) - printk("dtc_setup: usage dtc=address,irq\n"); - else if (commandline_current < NO_OVERRIDES) { - overrides[commandline_current].address = ints[1]; - overrides[commandline_current].irq = ints[2]; - for (i = 0; i < NO_BASES; ++i) - if (bases[i].address == ints[1]) { - bases[i].noauto = 1; - break; - } - ++commandline_current; - } - return 1; -} - -__setup("dtc=", dtc_setup); -#endif - -/* - * Function : int dtc_detect(struct scsi_host_template * tpnt) - * - * Purpose : detects and initializes DTC 3180/3280 controllers - * that were autoprobed, overridden on the LILO command line, - * or specified at compile time. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * -*/ - -static int __init dtc_detect(struct scsi_host_template * tpnt) -{ - static int current_override, current_base; - struct Scsi_Host *instance; - unsigned int addr; - void __iomem *base; - int sig, count; - - for (count = 0; current_override < NO_OVERRIDES; ++current_override) { - addr = 0; - base = NULL; - - if (overrides[current_override].address) { - addr = overrides[current_override].address; - base = ioremap(addr, 0x2000); - if (!base) - addr = 0; - } else - for (; !addr && (current_base < NO_BASES); ++current_base) { - dprintk(NDEBUG_INIT, "dtc: probing address 0x%08x\n", - (unsigned int)bases[current_base].address); - if (bases[current_base].noauto) - continue; - base = ioremap(bases[current_base].address, 0x2000); - if (!base) - continue; - for (sig = 0; sig < NO_SIGNATURES; ++sig) { - if (check_signature(base + signatures[sig].offset, signatures[sig].string, strlen(signatures[sig].string))) { - addr = bases[current_base].address; - dprintk(NDEBUG_INIT, "dtc: detected board\n"); - goto found; - } - } - iounmap(base); - } - - dprintk(NDEBUG_INIT, "dtc: addr = 0x%08x\n", addr); - - if (!addr) - break; - -found: - instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata)); - if (instance == NULL) - goto out_unmap; - - instance->base = addr; - ((struct NCR5380_hostdata *)(instance)->hostdata)->base = base; - - if (NCR5380_init(instance, FLAG_LATE_DMA_SETUP)) - goto out_unregister; - - NCR5380_maybe_reset_bus(instance); - - NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); /* Enable int's */ - if (overrides[current_override].irq != IRQ_AUTO) - instance->irq = overrides[current_override].irq; - else - instance->irq = NCR5380_probe_irq(instance, DTC_IRQS); - - /* Compatibility with documented NCR5380 kernel parameters */ - if (instance->irq == 255) - instance->irq = NO_IRQ; - - /* With interrupts enabled, it will sometimes hang when doing heavy - * reads. So better not enable them until I finger it out. */ - instance->irq = NO_IRQ; - - if (instance->irq != NO_IRQ) - if (request_irq(instance->irq, dtc_intr, 0, - "dtc", instance)) { - printk(KERN_ERR "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq); - instance->irq = NO_IRQ; - } - - if (instance->irq == NO_IRQ) { - printk(KERN_WARNING "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); - printk(KERN_WARNING "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); - } - - dprintk(NDEBUG_INIT, "scsi%d : irq = %d\n", - instance->host_no, instance->irq); - - ++current_override; - ++count; - } - return count; - -out_unregister: - scsi_unregister(instance); -out_unmap: - iounmap(base); - return count; -} - -/* - * Function : int dtc_biosparam(Disk * disk, struct block_device *dev, int *ip) - * - * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for - * the specified device / size. - * - * Inputs : size = size of device in sectors (512 bytes), dev = block device - * major / minor, ip[] = {heads, sectors, cylinders} - * - * Returns : always 0 (success), initializes ip - * -*/ - -/* - * XXX Most SCSI boards use this mapping, I could be incorrect. Some one - * using hard disks on a trantor should verify that this mapping corresponds - * to that used by the BIOS / ASPI driver by running the linux fdisk program - * and matching the H_C_S coordinates to what DOS uses. -*/ - -static int dtc_biosparam(struct scsi_device *sdev, struct block_device *dev, - sector_t capacity, int *ip) -{ - int size = capacity; - - ip[0] = 64; - ip[1] = 32; - ip[2] = size >> 11; - return 0; -} - - -/**************************************************************** - * Function : int NCR5380_pread (struct Scsi_Host *instance, - * unsigned char *dst, int len) - * - * Purpose : Fast 5380 pseudo-dma read function, reads len bytes to - * dst - * - * Inputs : dst = destination, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. -*/ - -static inline int dtc_pread(struct Scsi_Host *instance, - unsigned char *dst, int len) -{ - unsigned char *d = dst; - int i; /* For counting time spent in the poll-loop */ - struct NCR5380_hostdata *hostdata = shost_priv(instance); - - i = 0; - if (instance->irq == NO_IRQ) - NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ); - else - NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ | CSR_INT_BASE); - NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */ - rtrc(1); - while (len > 0) { - rtrc(2); - while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY) - ++i; - rtrc(3); - memcpy_fromio(d, hostdata->base + DTC_DATA_BUF, 128); - d += 128; - len -= 128; - rtrc(7); - /*** with int's on, it sometimes hangs after here. - * Looks like something makes HBNR go away. */ - } - rtrc(4); - while (!(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS)) - ++i; - rtrc(0); - return (0); -} - -/**************************************************************** - * Function : int NCR5380_pwrite (struct Scsi_Host *instance, - * unsigned char *src, int len) - * - * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from - * src - * - * Inputs : src = source, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. -*/ - -static inline int dtc_pwrite(struct Scsi_Host *instance, - unsigned char *src, int len) -{ - int i; - struct NCR5380_hostdata *hostdata = shost_priv(instance); - - if (instance->irq == NO_IRQ) - NCR5380_write(DTC_CONTROL_REG, 0); - else - NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); - NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */ - for (i = 0; len > 0; ++i) { - rtrc(5); - /* Poll until the host buffer can accept data. */ - while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY) - ++i; - rtrc(3); - memcpy_toio(hostdata->base + DTC_DATA_BUF, src, 128); - src += 128; - len -= 128; - } - rtrc(4); - while (!(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS)) - ++i; - rtrc(6); - /* Wait until the last byte has been sent to the disk */ - while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT)) - ++i; - rtrc(7); - /* Check for parity error here. fixme. */ - rtrc(0); - return (0); -} - -static int dtc_dma_xfer_len(struct scsi_cmnd *cmd) -{ - int transfersize = cmd->transfersize; - - /* Limit transfers to 32K, for xx400 & xx406 - * pseudoDMA that transfers in 128 bytes blocks. - */ - if (transfersize > 32 * 1024 && cmd->SCp.this_residual && - !(cmd->SCp.this_residual % transfersize)) - transfersize = 32 * 1024; - - return transfersize; -} - -MODULE_LICENSE("GPL"); - -#include "NCR5380.c" - -static int dtc_release(struct Scsi_Host *shost) -{ - struct NCR5380_hostdata *hostdata = shost_priv(shost); - - if (shost->irq != NO_IRQ) - free_irq(shost->irq, shost); - NCR5380_exit(shost); - scsi_unregister(shost); - iounmap(hostdata->base); - return 0; -} - -static struct scsi_host_template driver_template = { - .name = "DTC 3180/3280", - .detect = dtc_detect, - .release = dtc_release, - .proc_name = "dtc3x80", - .info = dtc_info, - .queuecommand = dtc_queue_command, - .eh_abort_handler = dtc_abort, - .eh_bus_reset_handler = dtc_bus_reset, - .bios_param = dtc_biosparam, - .can_queue = 32, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING, - .cmd_size = NCR5380_CMD_SIZE, - .max_sectors = 128, -}; -#include "scsi_module.c" diff --git a/drivers/scsi/dtc.h b/drivers/scsi/dtc.h deleted file mode 100644 index fcb0a8ea7bda..000000000000 --- a/drivers/scsi/dtc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * DTC controller, taken from T128 driver by... - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - */ - -#ifndef DTC3280_H -#define DTC3280_H - -#define NCR5380_implementation_fields \ - void __iomem *base - -#define DTC_address(reg) \ - (((struct NCR5380_hostdata *)shost_priv(instance))->base + DTC_5380_OFFSET + reg) - -#define NCR5380_read(reg) (readb(DTC_address(reg))) -#define NCR5380_write(reg, value) (writeb(value, DTC_address(reg))) - -#define NCR5380_dma_xfer_len(instance, cmd, phase) \ - dtc_dma_xfer_len(cmd) -#define NCR5380_dma_recv_setup dtc_pread -#define NCR5380_dma_send_setup dtc_pwrite -#define NCR5380_dma_residual(instance) (0) - -#define NCR5380_intr dtc_intr -#define NCR5380_queue_command dtc_queue_command -#define NCR5380_abort dtc_abort -#define NCR5380_bus_reset dtc_bus_reset -#define NCR5380_info dtc_info - -#define NCR5380_io_delay(x) udelay(x) - -/* 15 12 11 10 - 1001 1100 0000 0000 */ - -#define DTC_IRQS 0x9c00 - - -#endif /* DTC3280_H */ From 182d3f84307263e016977eaf23f9048483cf5ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Wed, 21 Sep 2016 22:54:07 +0200 Subject: [PATCH 170/173] scsi: fcoe: fix off by one in eth2fc_speed() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should be "< ARRAY_SIZE()" instead of "<= ARRAY_SIZE()". Fixes: 0b924e5505a568e7 ("scsi: fcoe: provide translation table between Ethernet and FC port speeds") Signed-off-by: Vincent Stehlé Cc: Johannes Thumshirn Cc: "Martin K. Petersen" Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/fcoe/fcoe_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index c164eec54308..375c536cbc68 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -110,7 +110,7 @@ static inline u32 eth2fc_speed(u32 eth_port_speed) { int i; - for (i = 0; i <= ARRAY_SIZE(fcoe_port_speed_mapping); i++) { + for (i = 0; i < ARRAY_SIZE(fcoe_port_speed_mapping); i++) { if (fcoe_port_speed_mapping[i].eth_port_speed == eth_port_speed) return fcoe_port_speed_mapping[i].fc_port_speed; } From 4bd173c30792791a6daca8c64793ec0a4ae8324f Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 23 Sep 2016 13:22:26 +0200 Subject: [PATCH 171/173] scsi: arcmsr: Simplify user_len checking Do the user_len check first and then the ver_addr allocation so that we can save us the kfree() on the error path when user_len is > ARCMSR_API_DATA_BUFLEN. Signed-off-by: Borislav Petkov Cc: Marco Grassi Cc: Dan Carpenter Cc: Tomas Henzl Cc: Martin K. Petersen Cc: Reviewed-by: Johannes Thumshirn Reviewed-by: Tomas Henzl Signed-off-by: Martin K. Petersen --- drivers/scsi/arcmsr/arcmsr_hba.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 110eca9eaca0..3d53d636b17b 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -2391,18 +2391,20 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, uint32_t user_len; int32_t cnt2end; uint8_t *pQbuffer, *ptmpuserbuffer; + + user_len = pcmdmessagefld->cmdmessage.Length; + if (user_len > ARCMSR_API_DATA_BUFLEN) { + retvalue = ARCMSR_MESSAGE_FAIL; + goto message_out; + } + ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC); if (!ver_addr) { retvalue = ARCMSR_MESSAGE_FAIL; goto message_out; } ptmpuserbuffer = ver_addr; - user_len = pcmdmessagefld->cmdmessage.Length; - if (user_len > ARCMSR_API_DATA_BUFLEN) { - retvalue = ARCMSR_MESSAGE_FAIL; - kfree(ver_addr); - goto message_out; - } + memcpy(ptmpuserbuffer, pcmdmessagefld->messagedatabuffer, user_len); spin_lock_irqsave(&acb->wqbuffer_lock, flags); From 7efa59e1608255a478dd62fc0e2e39f2563c05c2 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Fri, 23 Sep 2016 21:54:22 +0800 Subject: [PATCH 172/173] scsi: pm8001: Mark symbols static where possible We get 2 warnings when building kernel with W=1: drivers/scsi/pm8001/pm8001_sas.c:530:23: warning: no previous prototype for 'pm8001_alloc_dev' [-Wmissing-prototypes] drivers/scsi/pm8001/pm8001_hwi.c:4495:5: warning: no previous prototype for 'pm8001_chip_phy_stop_req' [-Wmissing-prototypes] In fact, these functions are only used in the file in which they are declared and don't need a declaration, but can be made static. So this patch marks these functions with 'static'. Signed-off-by: Baoyou Xie Acked-by: Lindar Liu Signed-off-by: Martin K. Petersen --- drivers/scsi/pm8001/pm8001_hwi.c | 4 ++-- drivers/scsi/pm8001/pm8001_sas.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 04e67a190652..10546faac58c 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -4492,8 +4492,8 @@ pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id) * @num: the inbound queue number * @phy_id: the phy id which we wanted to start up. */ -int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha, - u8 phy_id) +static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha, + u8 phy_id) { struct phy_stop_req payload; struct inbound_queue_table *circularQ; diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index dc33dfa8f994..ce584c31d36e 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -527,7 +527,7 @@ void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha, * pm8001_alloc_dev - find a empty pm8001_device * @pm8001_ha: our hba card information */ -struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha) +static struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha) { u32 dev; for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) { From 14bf41dcef651c13911a1715e83220732a3a4071 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Sun, 25 Sep 2016 13:48:50 +0800 Subject: [PATCH 173/173] scsi: mvsas: Mark symbols static where possible We get a few warnings when building kernel with W=1: drivers/scsi/mvsas/mv_sas.c:77:18: warning: no previous prototype for 'mvs_find_dev_mvi' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_sas.c:105:5: warning: no previous prototype for 'mvs_find_dev_phyno' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_sas.c:1161:20: warning: no previous prototype for 'mvs_alloc_dev' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_sas.c:1178:6: warning: no previous prototype for 'mvs_free_dev' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_sas.c:1188:5: warning: no previous prototype for 'mvs_dev_found_notify' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_sas.c:1244:6: warning: no previous prototype for 'mvs_dev_gone_notify' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_sas.c:1614:6: warning: no previous prototype for 'mvs_set_sense' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_sas.c:1653:6: warning: no previous prototype for 'mvs_fill_ssp_resp_iu' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_64xx.c:139:6: warning: no previous prototype for 'mvs_64xx_clear_srs_irq' [-Wmissing-prototypes] drivers/scsi/mvsas/mv_64xx.c:566:6: warning: no previous prototype for 'mvs_64xx_make_prd' [-Wmissing-prototypes] .... In fact, these functions are only used in the file in which they are declared and don't need a declaration, but can be made static. So this patch marks these functions with 'static'. Signed-off-by: Baoyou Xie Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/mvsas/mv_64xx.c | 19 +++++++++-------- drivers/scsi/mvsas/mv_94xx.c | 41 ++++++++++++++++++------------------ drivers/scsi/mvsas/mv_sas.c | 16 +++++++------- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c index 8bb06995adfb..b757d389e32f 100644 --- a/drivers/scsi/mvsas/mv_64xx.c +++ b/drivers/scsi/mvsas/mv_64xx.c @@ -136,7 +136,8 @@ static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard) } } -void mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) +static void +mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) { void __iomem *regs = mvi->regs; u32 tmp; @@ -563,7 +564,7 @@ static u8 mvs_64xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs) return MVS_ID_NOT_MAPPED; } -void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd) +static void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd) { int i; struct scatterlist *sg; @@ -633,7 +634,7 @@ static void mvs_64xx_phy_work_around(struct mvs_info *mvi, int i) mvs_write_port_vsr_data(mvi, i, tmp); } -void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, +static void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, struct sas_phy_linkrates *rates) { u32 lrmin = 0, lrmax = 0; @@ -668,20 +669,20 @@ static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi) } -u32 mvs_64xx_spi_read_data(struct mvs_info *mvi) +static u32 mvs_64xx_spi_read_data(struct mvs_info *mvi) { void __iomem *regs = mvi->regs_ex; return ior32(SPI_DATA_REG_64XX); } -void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data) +static void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data) { void __iomem *regs = mvi->regs_ex; iow32(SPI_DATA_REG_64XX, data); } -int mvs_64xx_spi_buildcmd(struct mvs_info *mvi, +static int mvs_64xx_spi_buildcmd(struct mvs_info *mvi, u32 *dwCmd, u8 cmd, u8 read, @@ -705,7 +706,7 @@ int mvs_64xx_spi_buildcmd(struct mvs_info *mvi, } -int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) +static int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) { void __iomem *regs = mvi->regs_ex; int retry; @@ -720,7 +721,7 @@ int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) return 0; } -int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) +static int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) { void __iomem *regs = mvi->regs_ex; u32 i, dwTmp; @@ -735,7 +736,7 @@ int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) return -1; } -void mvs_64xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, +static void mvs_64xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, int buf_len, int from, void *prd) { int i; diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c index f6fc4a705924..4c57d9abce7b 100644 --- a/drivers/scsi/mvsas/mv_94xx.c +++ b/drivers/scsi/mvsas/mv_94xx.c @@ -48,8 +48,8 @@ static void mvs_94xx_detect_porttype(struct mvs_info *mvi, int i) } } -void set_phy_tuning(struct mvs_info *mvi, int phy_id, - struct phy_tuning phy_tuning) +static void set_phy_tuning(struct mvs_info *mvi, int phy_id, + struct phy_tuning phy_tuning) { u32 tmp, setting_0 = 0, setting_1 = 0; u8 i; @@ -110,8 +110,8 @@ void set_phy_tuning(struct mvs_info *mvi, int phy_id, } } -void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id, - struct ffe_control ffe) +static void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id, + struct ffe_control ffe) { u32 tmp; @@ -177,7 +177,7 @@ void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id, } /*Notice: this function must be called when phy is disabled*/ -void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate) +static void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate) { union reg_phy_cfg phy_cfg, phy_cfg_tmp; mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2); @@ -679,7 +679,8 @@ static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx) } } -void mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) +static void +mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) { void __iomem *regs = mvi->regs; u32 tmp; @@ -906,8 +907,8 @@ static void mvs_94xx_fix_phy_info(struct mvs_info *mvi, int i, } -void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, - struct sas_phy_linkrates *rates) +static void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, + struct sas_phy_linkrates *rates) { u32 lrmax = 0; u32 tmp; @@ -936,25 +937,25 @@ static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi) } -u32 mvs_94xx_spi_read_data(struct mvs_info *mvi) +static u32 mvs_94xx_spi_read_data(struct mvs_info *mvi) { void __iomem *regs = mvi->regs_ex - 0x10200; return mr32(SPI_RD_DATA_REG_94XX); } -void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data) +static void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data) { void __iomem *regs = mvi->regs_ex - 0x10200; mw32(SPI_RD_DATA_REG_94XX, data); } -int mvs_94xx_spi_buildcmd(struct mvs_info *mvi, - u32 *dwCmd, - u8 cmd, - u8 read, - u8 length, - u32 addr +static int mvs_94xx_spi_buildcmd(struct mvs_info *mvi, + u32 *dwCmd, + u8 cmd, + u8 read, + u8 length, + u32 addr ) { void __iomem *regs = mvi->regs_ex - 0x10200; @@ -974,7 +975,7 @@ int mvs_94xx_spi_buildcmd(struct mvs_info *mvi, } -int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) +static int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) { void __iomem *regs = mvi->regs_ex - 0x10200; mw32(SPI_CTRL_REG_94XX, cmd | SPI_CTRL_SpiStart_94XX); @@ -982,7 +983,7 @@ int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) return 0; } -int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) +static int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) { void __iomem *regs = mvi->regs_ex - 0x10200; u32 i, dwTmp; @@ -997,8 +998,8 @@ int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) return -1; } -void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, - int buf_len, int from, void *prd) +static void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, + int buf_len, int from, void *prd) { int i; struct mvs_prd *buf_prd = prd; diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 5b9fcff6cd94..86eb19902bac 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -74,7 +74,7 @@ void mvs_tag_init(struct mvs_info *mvi) mvs_tag_clear(mvi, i); } -struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev) +static struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev) { unsigned long i = 0, j = 0, hi = 0; struct sas_ha_struct *sha = dev->port->ha; @@ -102,7 +102,7 @@ struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev) } -int mvs_find_dev_phyno(struct domain_device *dev, int *phyno) +static int mvs_find_dev_phyno(struct domain_device *dev, int *phyno) { unsigned long i = 0, j = 0, n = 0, num = 0; struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev; @@ -1158,7 +1158,7 @@ void mvs_port_deformed(struct asd_sas_phy *sas_phy) mvs_port_notify_deformed(sas_phy, 1); } -struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi) +static struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi) { u32 dev; for (dev = 0; dev < MVS_MAX_DEVICES; dev++) { @@ -1175,7 +1175,7 @@ struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi) return NULL; } -void mvs_free_dev(struct mvs_device *mvi_dev) +static void mvs_free_dev(struct mvs_device *mvi_dev) { u32 id = mvi_dev->device_id; memset(mvi_dev, 0, sizeof(*mvi_dev)); @@ -1185,7 +1185,7 @@ void mvs_free_dev(struct mvs_device *mvi_dev) mvi_dev->taskfileset = MVS_ID_NOT_MAPPED; } -int mvs_dev_found_notify(struct domain_device *dev, int lock) +static int mvs_dev_found_notify(struct domain_device *dev, int lock) { unsigned long flags = 0; int res = 0; @@ -1241,7 +1241,7 @@ int mvs_dev_found(struct domain_device *dev) return mvs_dev_found_notify(dev, 1); } -void mvs_dev_gone_notify(struct domain_device *dev) +static void mvs_dev_gone_notify(struct domain_device *dev) { unsigned long flags = 0; struct mvs_device *mvi_dev = dev->lldd_dev; @@ -1611,7 +1611,7 @@ static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task, return stat; } -void mvs_set_sense(u8 *buffer, int len, int d_sense, +static void mvs_set_sense(u8 *buffer, int len, int d_sense, int key, int asc, int ascq) { memset(buffer, 0, len); @@ -1650,7 +1650,7 @@ void mvs_set_sense(u8 *buffer, int len, int d_sense, return; } -void mvs_fill_ssp_resp_iu(struct ssp_response_iu *iu, +static void mvs_fill_ssp_resp_iu(struct ssp_response_iu *iu, u8 key, u8 asc, u8 asc_q) { iu->datapres = 2;