Add function methods for tmf's. Signed-off-by: John Garry <john.garry@xxxxxxxxxx> --- drivers/scsi/hisi_sas/hisi_sas.h | 7 + drivers/scsi/hisi_sas/hisi_sas_init.c | 6 + drivers/scsi/hisi_sas/hisi_sas_main.c | 385 ++++++++++++++++++++++++++++++++++ 3 files changed, 398 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 9565a2a..b488fe3 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -360,6 +360,13 @@ void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int i); int hisi_sas_dev_found(struct domain_device *dev); void hisi_sas_dev_gone(struct domain_device *dev); int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags); +int hisi_sas_abort_task(struct sas_task *task); +int hisi_sas_abort_task_set(struct domain_device *dev, u8 *lun); +int hisi_sas_clear_aca(struct domain_device *dev, u8 *lun); +int hisi_sas_clear_task_set(struct domain_device *dev, u8 *lun); +int hisi_sas_I_T_nexus_reset(struct domain_device *dev); +int hisi_sas_lu_reset(struct domain_device *dev, u8 *lun); +int hisi_sas_query_task(struct sas_task *task); void hisi_sas_port_formed(struct asd_sas_phy *sas_phy); void hisi_sas_port_deformed(struct asd_sas_phy *sas_phy); void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy); diff --git a/drivers/scsi/hisi_sas/hisi_sas_init.c b/drivers/scsi/hisi_sas/hisi_sas_init.c index caab645..b6272d8 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_init.c +++ b/drivers/scsi/hisi_sas/hisi_sas_init.c @@ -38,6 +38,12 @@ static struct sas_domain_function_template hisi_sas_transport_ops = { .lldd_dev_found = hisi_sas_dev_found, .lldd_dev_gone = hisi_sas_dev_gone, .lldd_execute_task = hisi_sas_queue_command, + .lldd_abort_task = hisi_sas_abort_task, + .lldd_abort_task_set = hisi_sas_abort_task_set, + .lldd_clear_aca = hisi_sas_clear_aca, + .lldd_I_T_nexus_reset = hisi_sas_I_T_nexus_reset, + .lldd_lu_reset = hisi_sas_lu_reset, + .lldd_query_task = hisi_sas_query_task, .lldd_port_formed = hisi_sas_port_formed, .lldd_port_deformed = hisi_sas_port_deformed, }; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 393d22a..12af29c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -23,6 +23,19 @@ static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device) return device->port->ha->lldd_ha; } +static int hisi_sas_find_tag(struct hisi_hba *hisi_hba, + struct sas_task *task, u32 *tag) +{ + if (task->lldd_task) { + struct hisi_sas_slot *slot; + + slot = task->lldd_task; + *tag = slot->idx; + return 1; + } + return 0; +} + static void hisi_sas_slot_index_clear(struct hisi_hba *hisi_hba, int slot_idx) { void *bitmap = hisi_hba->slot_index_tags; @@ -599,6 +612,44 @@ int hisi_sas_dev_found(struct domain_device *device) return hisi_sas_dev_found_notify(device, 1); } +int hisi_sas_find_dev_phyno(struct domain_device *device, int *phyno) +{ + int i = 0, j = 0, num = 0, n = 0; + struct sas_ha_struct *sha = device->port->ha; + + while (sha->sas_port[i]) { + if (sha->sas_port[i] == device->port) { + struct asd_sas_phy *phy; + + list_for_each_entry(phy, + &sha->sas_port[i]->phy_list, port_phy_el) { + j = 0; + + while (sha->sas_phy[j]) { + if (sha->sas_phy[j] == phy) + break; + j++; + } + phyno[n] = j; + num++; + n++; + } + break; + } + i++; + } + return num; +} + +static void hisi_sas_release_task(struct hisi_hba *hisi_hba, + struct domain_device *device) +{ + int i, phyno[4], num; + + num = hisi_sas_find_dev_phyno(device, phyno); + for (i = 0; i < num; i++) + hisi_sas_do_release_task(hisi_hba, phyno[i], device); +} static void hisi_sas_dev_gone_notify(struct domain_device *device) { @@ -630,6 +681,340 @@ int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags) return hisi_sas_task_exec(task, gfp_flags, NULL, 0, NULL); } + +static void hisi_sas_task_done(struct sas_task *task) +{ + if (!del_timer(&task->slow_task->timer)) + return; + complete(&task->slow_task->completion); +} + +static void hisi_sas_tmf_timedout(unsigned long data) +{ + struct sas_task *task = (struct sas_task *)data; + + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + complete(&task->slow_task->completion); +} + +#define TASK_TIMEOUT 20 +static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, + void *parameter, + u32 para_len, + struct hisi_sas_tmf_task *tmf) +{ + int res, retry; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct hisi_hba *hisi_hba = sas_dev->hisi_hba; + struct device *dev = &hisi_hba->pdev->dev; + struct sas_task *task = NULL; + + for (retry = 0; retry < 3; retry++) { + task = sas_alloc_slow_task(GFP_KERNEL); + if (!task) + return -ENOMEM; + + task->dev = device; + task->task_proto = device->tproto; + + memcpy(&task->ssp_task, parameter, para_len); + 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 + TASK_TIMEOUT*HZ; + add_timer(&task->slow_task->timer); + + res = hisi_sas_task_exec(task, GFP_KERNEL, NULL, 1, tmf); + + if (res) { + del_timer(&task->slow_task->timer); + dev_err(dev, "executing internal task failed: %d\n", + res); + goto ex_err; + } + + wait_for_completion(&task->slow_task->completion); + res = TMF_RESP_FUNC_FAILED; + /* Even 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, "TMF task[%d] timeout\n", + tmf->tag_of_task_to_be_managed); + if (task->lldd_task) { + struct hisi_sas_slot *slot; + + slot = (struct hisi_sas_slot *) + task->lldd_task; + hisi_sas_slot_task_free(hisi_hba, + task, slot); + } + + goto ex_err; + } + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAM_STAT_GOOD) { + res = TMF_RESP_FUNC_COMPLETE; + 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 + * underrun */ + pr_warn(" ok: task to dev %016llx response: 0x%x " + "status 0x%x underrun\n", + SAS_ADDR(device->sas_addr), + task->task_status.resp, + task->task_status.stat); + res = task->task_status.residual; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_OVERRUN) { + pr_warn("%s: blocked task error\n", __func__); + res = -EMSGSIZE; + break; + } + + pr_warn("%s: task to dev %016llx response: 0x%x " + "status 0x%x\n", __func__, + SAS_ADDR(device->sas_addr), + task->task_status.resp, + task->task_status.stat); + sas_free_task(task); + task = NULL; + } +ex_err: + BUG_ON(retry == 3 && task != NULL); + sas_free_task(task); + return res; +} + +static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device, + u8 *lun, struct hisi_sas_tmf_task *tmf) +{ + struct sas_ssp_task ssp_task; + + if (!(device->tproto & SAS_PROTOCOL_SSP)) + return TMF_RESP_FUNC_ESUPP; + + memcpy(ssp_task.LUN, lun, 8); + + return hisi_sas_exec_internal_tmf_task(device, &ssp_task, + sizeof(ssp_task), tmf); +} + +int hisi_sas_abort_task(struct sas_task *task) +{ + struct scsi_lun lun; + struct hisi_sas_tmf_task tmf_task; + struct domain_device *device = task->dev; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct hisi_hba *hisi_hba; + struct device *dev; + int rc = TMF_RESP_FUNC_FAILED; + unsigned long flags; + u32 tag; + + if (!sas_dev) { + pr_warn("%s: Device has been removed\n", __func__); + return TMF_RESP_FUNC_FAILED; + } + + hisi_hba = dev_to_hisi_hba(task->dev); + dev = &hisi_hba->pdev->dev; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + rc = TMF_RESP_FUNC_COMPLETE; + goto out; + } + + spin_unlock_irqrestore(&task->task_state_lock, flags); + sas_dev->dev_status = HISI_SAS_DEV_EH; + if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) { + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)task->uldd_task; + + int_to_scsilun(cmnd->device->lun, &lun); + rc = hisi_sas_find_tag(hisi_hba, task, &tag); + if (rc == 0) { + dev_notice(dev, "abort task: No such tag\n"); + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + + tmf_task.tmf = TMF_ABORT_TASK; + tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag); + + rc = hisi_sas_debug_issue_ssp_tmf(task->dev, + lun.scsi_lun, + &tmf_task); + + /* if successful, clear the task and callback forwards.*/ + if (rc == TMF_RESP_FUNC_COMPLETE) { + if (task->lldd_task) { + struct hisi_sas_slot *slot; + + slot = &hisi_hba->slot_info + [tmf_task.tag_of_task_to_be_managed]; + spin_lock_irqsave(&hisi_hba->lock, flags); + slot_complete_v1_hw(hisi_hba, slot, 1); + spin_unlock_irqrestore(&hisi_hba->lock, flags); + } + } + + } else if (task->task_proto & SAS_PROTOCOL_SATA || + task->task_proto & SAS_PROTOCOL_STP) { + if (SAS_SATA_DEV == task->dev->dev_type) { + 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; + rc = TMF_RESP_FUNC_COMPLETE; + goto out; + } + + } + +out: + if (rc != TMF_RESP_FUNC_COMPLETE) + dev_notice(dev, "abort task: rc=%d\n", rc); + return rc; +} + +int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct hisi_sas_tmf_task tmf_task; + + tmf_task.tmf = TMF_ABORT_TASK_SET; + rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); + + return rc; +} + +int hisi_sas_clear_aca(struct domain_device *device, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct hisi_sas_tmf_task tmf_task; + + tmf_task.tmf = TMF_CLEAR_ACA; + rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); + + return rc; +} + +int hisi_sas_clear_task_set(struct domain_device *device, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct hisi_sas_tmf_task tmf_task; + + tmf_task.tmf = TMF_CLEAR_TASK_SET; + rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); + + return rc; +} + +static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device) +{ + int rc; + struct sas_phy *phy = sas_get_local_phy(device); + int reset_type = (device->dev_type == SAS_SATA_DEV || + (device->tproto & SAS_PROTOCOL_STP)) ? 0 : 1; + rc = sas_phy_reset(phy, reset_type); + sas_put_local_phy(phy); + msleep(2000); + return rc; +} + +int hisi_sas_I_T_nexus_reset(struct domain_device *device) +{ + unsigned long flags; + int rc = TMF_RESP_FUNC_FAILED; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); + + if (sas_dev->dev_status != HISI_SAS_DEV_EH) + return TMF_RESP_FUNC_FAILED; + sas_dev->dev_status = HISI_SAS_DEV_NORMAL; + + rc = hisi_sas_debug_I_T_nexus_reset(device); + + spin_lock_irqsave(&hisi_hba->lock, flags); + hisi_sas_release_task(hisi_hba, device); + spin_unlock_irqrestore(&hisi_hba->lock, flags); + + return 0; +} + +int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) +{ + unsigned long flags; + int rc = TMF_RESP_FUNC_FAILED; + struct hisi_sas_tmf_task tmf_task; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); + struct device *dev = &hisi_hba->pdev->dev; + + tmf_task.tmf = TMF_LU_RESET; + sas_dev->dev_status = HISI_SAS_DEV_EH; + rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); + if (rc == TMF_RESP_FUNC_COMPLETE) { + spin_lock_irqsave(&hisi_hba->lock, flags); + hisi_sas_release_task(hisi_hba, device); + spin_unlock_irqrestore(&hisi_hba->lock, flags); + } + /* If failed, fall-through I_T_Nexus reset */ + dev_err(dev, "lu_reset: for device[%llx]:rc= %d\n", + sas_dev->device_id, rc); + return rc; +} + +int hisi_sas_query_task(struct sas_task *task) +{ + u32 tag; + struct scsi_lun lun; + struct hisi_sas_tmf_task tmf_task; + int rc = TMF_RESP_FUNC_FAILED; + + if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) { + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)task->uldd_task; + struct domain_device *device = task->dev; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct hisi_hba *hisi_hba = sas_dev->hisi_hba; + + int_to_scsilun(cmnd->device->lun, &lun); + rc = hisi_sas_find_tag(hisi_hba, task, &tag); + if (rc == 0) { + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + + tmf_task.tmf = TMF_QUERY_TASK; + tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag); + + rc = hisi_sas_debug_issue_ssp_tmf(device, + lun.scsi_lun, + &tmf_task); + switch (rc) { + /* The task is still in Lun, release it then */ + case TMF_RESP_FUNC_SUCC: + /* The task is not in Lun or failed, reset the phy */ + case TMF_RESP_FUNC_FAILED: + case TMF_RESP_FUNC_COMPLETE: + break; + } + } + pr_info("%s: rc=%d\n", __func__, rc); + return rc; +} + void hisi_sas_port_formed(struct asd_sas_phy *sas_phy) { hisi_sas_port_notify_formed(sas_phy, 1); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html