From patchwork Wed Aug 10 13:19:58 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Garry X-Patchwork-Id: 73699 Delivered-To: patch@linaro.org Received: by 10.140.29.52 with SMTP id a49csp588660qga; Wed, 10 Aug 2016 13:50:41 -0700 (PDT) X-Received: by 10.66.142.105 with SMTP id rv9mr10422234pab.33.1470862238384; Wed, 10 Aug 2016 13:50:38 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id v187si50220228pfb.258.2016.08.10.13.50.38; Wed, 10 Aug 2016 13:50:38 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933374AbcHJUtw (ORCPT + 27 others); Wed, 10 Aug 2016 16:49:52 -0400 Received: from szxga02-in.huawei.com ([119.145.14.65]:49106 "EHLO szxga02-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932932AbcHJSL5 (ORCPT ); Wed, 10 Aug 2016 14:11:57 -0400 Received: from 172.24.1.36 (EHLO szxeml432-hub.china.huawei.com) ([172.24.1.36]) by szxrg02-dlp.huawei.com (MOS 4.3.7-GA FastPath queued) with ESMTP id DLN22375; Wed, 10 Aug 2016 21:04:37 +0800 (CST) Received: from localhost.localdomain (10.67.212.75) by szxeml432-hub.china.huawei.com (10.82.67.209) with Microsoft SMTP Server id 14.3.235.1; Wed, 10 Aug 2016 21:04:29 +0800 From: John Garry To: , CC: , , , , , , John Garry Subject: [PATCH 1/8] hisi_sas: add internal abort core code Date: Wed, 10 Aug 2016 21:19:58 +0800 Message-ID: <1470835205-65684-2-git-send-email-john.garry@huawei.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1470835205-65684-1-git-send-email-john.garry@huawei.com> References: <1470835205-65684-1-git-send-email-john.garry@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.67.212.75] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020203.57AB2665.01CD, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2013-06-18 04:22:30, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: 814744f676f75d3c71bd7815c2fa2611 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add core 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 --- drivers/scsi/hisi_sas/hisi_sas.h | 3 + drivers/scsi/hisi_sas/hisi_sas_main.c | 154 ++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) -- 1.9.1 diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 4731d32..4ae864d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -146,6 +146,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 18dd5ea..d30ece2 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,148 @@ 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; +} + +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);