From patchwork Mon Feb 27 19:54:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Philippe Brucker X-Patchwork-Id: 9594067 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 00F8260453 for ; Mon, 27 Feb 2017 20:25:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E61F1281E1 for ; Mon, 27 Feb 2017 20:25:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D97BB2832D; Mon, 27 Feb 2017 20:25:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5EFF5281E1 for ; Mon, 27 Feb 2017 20:25:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751557AbdB0UZ2 (ORCPT ); Mon, 27 Feb 2017 15:25:28 -0500 Received: from foss.arm.com ([217.140.101.70]:58758 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751552AbdB0UYm (ORCPT ); Mon, 27 Feb 2017 15:24:42 -0500 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 99D7E1684; Mon, 27 Feb 2017 11:59:10 -0800 (PST) Received: from e106794-lin.cambridge.arm.com (e106794-lin.cambridge.arm.com [10.1.210.60]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id EE80D3F3E1; Mon, 27 Feb 2017 11:59:07 -0800 (PST) From: Jean-Philippe Brucker Cc: Harv Abdulhamid , Will Deacon , Shanker Donthineni , Bjorn Helgaas , Sinan Kaya , Lorenzo Pieralisi , Catalin Marinas , Robin Murphy , Joerg Roedel , Nate Watterson , Alex Williamson , David Woodhouse , linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org, iommu@lists.linux-foundation.org, kvm@vger.kernel.org Subject: [RFC PATCH 23/30] iommu/arm-smmu-v3: Bind/unbind device and task Date: Mon, 27 Feb 2017 19:54:34 +0000 Message-Id: <20170227195441.5170-24-jean-philippe.brucker@arm.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170227195441.5170-1-jean-philippe.brucker@arm.com> References: <20170227195441.5170-1-jean-philippe.brucker@arm.com> To: unlisted-recipients:; (no To-header on input) Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Now that everything is in place, implement bind and unbind operations. Signed-off-by: Jean-Philippe Brucker --- drivers/iommu/arm-smmu-v3.c | 178 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index 5e0008ac68cb..3ba7f65020f9 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -742,6 +742,7 @@ struct arm_smmu_master_data { bool can_fault; u32 avail_contexts; + const struct iommu_svm_ops *svm_ops; }; /* SMMU private data for an IOMMU domain */ @@ -820,6 +821,7 @@ struct arm_smmu_context { struct arm_smmu_task *task; struct arm_smmu_master_data *master; + void *priv; struct list_head task_head; struct rb_node master_node; @@ -2085,6 +2087,26 @@ static size_t arm_smmu_atc_invalidate_task(struct arm_smmu_task *smmu_task, return size; } +static size_t arm_smmu_atc_invalidate_context(struct arm_smmu_context *smmu_context, + unsigned long iova, size_t size) +{ + struct arm_smmu_cmdq_ent cmd; + struct arm_smmu_device *smmu = smmu_context->master->smmu; + struct arm_smmu_cmdq_ent sync_cmd = { + .opcode = CMDQ_OP_CMD_SYNC, + }; + + arm_smmu_atc_invalidate_to_cmd(smmu, iova, size, &cmd); + + cmd.substream_valid = true; + cmd.atc.ssid = smmu_context->ssid; + + arm_smmu_atc_invalidate_master(smmu_context->master, &cmd); + arm_smmu_cmdq_issue_cmd(smmu, &sync_cmd); + + return size; +} + /* IOMMU API */ static bool arm_smmu_capable(enum iommu_cap cap) { @@ -2098,7 +2120,6 @@ static bool arm_smmu_capable(enum iommu_cap cap) } } -__maybe_unused static struct arm_smmu_context * arm_smmu_attach_task(struct arm_smmu_task *smmu_task, struct arm_smmu_master_data *master) @@ -2444,7 +2465,6 @@ static void arm_smmu_free_task_pgtable(struct arm_smmu_task *smmu_task) arm_smmu_bitmap_free(smmu->asid_map, smmu_task->s1_cfg.asid); } -__maybe_unused static struct arm_smmu_task *arm_smmu_alloc_task(struct arm_smmu_device *smmu, struct task_struct *task) { @@ -2741,7 +2761,156 @@ static void arm_smmu_handle_fault(struct work_struct *work) static bool arm_smmu_master_supports_svm(struct arm_smmu_master_data *master) { - return false; + return dev_is_pci(master->dev) && master->can_fault && + master->avail_contexts; +} + +static int arm_smmu_set_svm_ops(struct device *dev, + const struct iommu_svm_ops *svm_ops) +{ + struct arm_smmu_master_data *master; + + if (!dev->iommu_fwspec) + return -EINVAL; + + master = dev->iommu_fwspec->iommu_priv; + if (!master) + return -EINVAL; + + master->svm_ops = svm_ops; + + return 0; +} + +static int arm_smmu_bind_task(struct device *dev, struct task_struct *task, + int *pasid, int flags, void *priv) +{ + int ret = 0; + struct pid *pid; + struct iommu_group *group; + struct arm_smmu_device *smmu; + struct arm_smmu_group *smmu_group; + struct arm_smmu_domain *smmu_domain; + struct arm_smmu_master_data *master; + struct arm_smmu_task *smmu_task = NULL, *cur_task; + struct arm_smmu_context *smmu_context = NULL, *cur_context; + + if (!dev->iommu_fwspec) + return -EINVAL; + + master = dev->iommu_fwspec->iommu_priv; + if (!master) + return -EINVAL; + + if (!arm_smmu_master_supports_svm(master)) + return -EINVAL; + + smmu = master->smmu; + + group = iommu_group_get(dev); + smmu_group = to_smmu_group(group); + + smmu_domain = smmu_group->domain; + if (!smmu_domain) { + iommu_group_put(group); + return -EINVAL; + } + + if (smmu_domain->stage != ARM_SMMU_DOMAIN_S1) { + /* We do not support stage-2 SVM yet... */ + iommu_group_put(group); + return -ENOSYS; + } + + iommu_group_put(group); + + pid = get_task_pid(task, PIDTYPE_PID); + + spin_lock(&smmu->contexts_lock); + + list_for_each_entry(cur_task, &smmu->tasks, smmu_head) { + if (cur_task->pid == pid) { + kref_get(&cur_task->kref); + smmu_task = cur_task; + break; + } + } + + if (smmu_task) { + list_for_each_entry(cur_context, &smmu_task->contexts, + task_head) { + if (cur_context->master->dev == dev) { + smmu_context = cur_context; + _arm_smmu_put_task(cur_task); + break; + } + } + } + spin_unlock(&smmu->contexts_lock); + + put_pid(pid); + + if (smmu_context) + /* We don't support nested bind/unbind calls */ + return -EEXIST; + + if (!smmu_task) { + smmu_task = arm_smmu_alloc_task(smmu, task); + if (IS_ERR(smmu_task)) + return -PTR_ERR(smmu_task); + } + + smmu_context = arm_smmu_attach_task(smmu_task, master); + if (IS_ERR(smmu_context)) { + arm_smmu_put_task(smmu, smmu_task); + return PTR_ERR(smmu_context); + } + + smmu_context->priv = priv; + + *pasid = smmu_context->ssid; + dev_dbg(dev, "bound to task %d with PASID %d\n", pid_vnr(pid), *pasid); + + return ret; +} + +static int arm_smmu_unbind_task(struct device *dev, int pasid, int flags) +{ + struct arm_smmu_device *smmu; + struct arm_smmu_master_data *master; + struct arm_smmu_context *smmu_context = NULL; + + if (!dev->iommu_fwspec) + return -EINVAL; + + master = dev->iommu_fwspec->iommu_priv; + if (!master) + return -EINVAL; + + smmu = master->smmu; + + smmu_context = arm_smmu_get_context_by_id(smmu, master, 0, pasid); + if (!smmu_context) + return -ESRCH; + + dev_dbg(dev, "unbind PASID %d\n", pasid); + + /* + * There isn't any "ATC invalidate all by PASID" command. If this isn't + * good enough, we'll need fine-grained invalidation for each vma. + */ + arm_smmu_atc_invalidate_context(smmu_context, 0, -1); + + spin_lock(&smmu->contexts_lock); + if (smmu_context->task) + arm_smmu_detach_task(smmu_context); + + /* Release the ref we got earlier in this function */ + _arm_smmu_put_context(smmu_context); + _arm_smmu_put_context(smmu_context); + spin_unlock(&smmu->contexts_lock); + + return 0; } static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) @@ -3626,6 +3795,9 @@ static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, .domain_alloc = arm_smmu_domain_alloc, .domain_free = arm_smmu_domain_free, + .set_svm_ops = arm_smmu_set_svm_ops, + .bind_task = arm_smmu_bind_task, + .unbind_task = arm_smmu_unbind_task, .attach_dev = arm_smmu_attach_dev, .map = arm_smmu_map, .unmap = arm_smmu_unmap,