From patchwork Mon Dec 15 00:13:46 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 5489611 X-Patchwork-Delegate: geert@linux-m68k.org Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 45E9F9F1D4 for ; Mon, 15 Dec 2014 00:14:04 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2544820A03 for ; Mon, 15 Dec 2014 00:14:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F35C3209FD for ; Mon, 15 Dec 2014 00:14:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751981AbaLOAN7 (ORCPT ); Sun, 14 Dec 2014 19:13:59 -0500 Received: from galahad.ideasonboard.com ([185.26.127.97]:46336 "EHLO galahad.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752011AbaLOAN6 (ORCPT ); Sun, 14 Dec 2014 19:13:58 -0500 Received: from avalon.ideasonboard.com (dsl-hkibrasgw3-50ddcc-40.dhcp.inet.fi [80.221.204.40]) by galahad.ideasonboard.com (Postfix) with ESMTPSA id 2556E20BDF; Mon, 15 Dec 2014 01:10:40 +0100 (CET) From: Laurent Pinchart To: iommu@lists.linux-foundation.org Cc: linux-sh@vger.kernel.org Subject: [PATCH 07/10] iommu/ipmmu-vmsa: Support multiple micro TLBs per device Date: Mon, 15 Dec 2014 02:13:46 +0200 Message-Id: <1418602429-11276-8-git-send-email-laurent.pinchart+renesas@ideasonboard.com> X-Mailer: git-send-email 2.0.4 In-Reply-To: <1418602429-11276-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com> References: <1418602429-11276-1-git-send-email-laurent.pinchart+renesas@ideasonboard.com> Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Devices such as the system DMA controller are connected to multiple micro TLBs of the same IOMMU. Support this. Selective enabling of micro TLBs based on runtime device usage isn't possible at the moment due to lack of support in the IOMMU and DMA mapping APIs. Support for devices connected to different IOMMUs is also unsupported for the same reason. Signed-off-by: Laurent Pinchart --- drivers/iommu/ipmmu-vmsa.c | 115 +++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 29 deletions(-) diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index dbbdb76d5a6b..5a99fad5c30f 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -47,7 +47,8 @@ struct ipmmu_vmsa_domain { struct ipmmu_vmsa_archdata { struct ipmmu_vmsa_device *mmu; - unsigned int utlb; + unsigned int *utlbs; + unsigned int num_utlbs; }; static DEFINE_SPINLOCK(ipmmu_devices_lock); @@ -898,6 +899,7 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, struct ipmmu_vmsa_device *mmu = archdata->mmu; struct ipmmu_vmsa_domain *domain = io_domain->priv; unsigned long flags; + unsigned int i; int ret = 0; if (!mmu) { @@ -926,7 +928,8 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, if (ret < 0) return ret; - ipmmu_utlb_enable(domain, archdata->utlb); + for (i = 0; i < archdata->num_utlbs; ++i) + ipmmu_utlb_enable(domain, archdata->utlbs[i]); return 0; } @@ -936,8 +939,10 @@ static void ipmmu_detach_device(struct iommu_domain *io_domain, { struct ipmmu_vmsa_archdata *archdata = dev->archdata.iommu; struct ipmmu_vmsa_domain *domain = io_domain->priv; + unsigned int i; - ipmmu_utlb_disable(domain, archdata->utlb); + for (i = 0; i < archdata->num_utlbs; ++i) + ipmmu_utlb_disable(domain, archdata->utlbs[i]); /* * TODO: Optimize by disabling the context when no device is attached. @@ -1001,10 +1006,12 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); } -static int ipmmu_find_utlb(struct ipmmu_vmsa_device *mmu, struct device *dev) +static int ipmmu_find_utlbs(struct ipmmu_vmsa_device *mmu, struct device *dev, + unsigned int **_utlbs) { - struct of_phandle_args args; - int ret; + unsigned int *utlbs; + unsigned int i; + int count; if (mmu->pdata) { const struct ipmmu_vmsa_master *master = mmu->pdata->masters; @@ -1012,32 +1019,64 @@ static int ipmmu_find_utlb(struct ipmmu_vmsa_device *mmu, struct device *dev) unsigned int i; for (i = 0; i < mmu->pdata->num_masters; ++i, ++master) { - if (strcmp(master->name, devname) == 0) - return master->utlb; + if (strcmp(master->name, devname) == 0) { + utlbs = kmalloc(sizeof(*utlbs), GFP_KERNEL); + if (!utlbs) + return -ENOMEM; + + utlbs[0] = master->utlb; + + *_utlbs = utlbs; + return 1; + } } - return -1; + return -EINVAL; } - ret = of_parse_phandle_with_args(dev->of_node, "iommus", - "#iommu-cells", 0, &args); - if (ret < 0) - return -1; + count = of_count_phandle_with_args(dev->of_node, "iommus", + "#iommu-cells"); + if (count < 0) + return -EINVAL; + + utlbs = kcalloc(count, sizeof(*utlbs), GFP_KERNEL); + if (!utlbs) + return -ENOMEM; + + for (i = 0; i < count; ++i) { + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_args(dev->of_node, "iommus", + "#iommu-cells", i, &args); + if (ret < 0) + goto error; + + of_node_put(args.np); + + if (args.np != mmu->dev->of_node || args.args_count != 1) + goto error; + + utlbs[i] = args.args[0]; + } - of_node_put(args.np); + *_utlbs = utlbs; - if (args.np != mmu->dev->of_node || args.args_count != 1) - return -1; + return count; - return args.args[0]; +error: + kfree(utlbs); + return -EINVAL; } static int ipmmu_add_device(struct device *dev) { struct ipmmu_vmsa_archdata *archdata; struct ipmmu_vmsa_device *mmu; - struct iommu_group *group; - int utlb = -1; + struct iommu_group *group = NULL; + unsigned int *utlbs = NULL; + unsigned int i; + int num_utlbs = 0; int ret; if (dev->archdata.iommu) { @@ -1050,8 +1089,8 @@ static int ipmmu_add_device(struct device *dev) spin_lock(&ipmmu_devices_lock); list_for_each_entry(mmu, &ipmmu_devices, list) { - utlb = ipmmu_find_utlb(mmu, dev); - if (utlb >= 0) { + num_utlbs = ipmmu_find_utlbs(mmu, dev, &utlbs); + if (num_utlbs) { /* * TODO Take a reference to the MMU to protect * against device removal. @@ -1062,17 +1101,22 @@ static int ipmmu_add_device(struct device *dev) spin_unlock(&ipmmu_devices_lock); - if (utlb < 0) + if (num_utlbs <= 0) return -ENODEV; - if (utlb >= mmu->num_utlbs) - return -EINVAL; + for (i = 0; i < num_utlbs; ++i) { + if (utlbs[i] >= mmu->num_utlbs) { + ret = -EINVAL; + goto error; + } + } /* Create a device group and add the device to it. */ group = iommu_group_alloc(); if (IS_ERR(group)) { dev_err(dev, "Failed to allocate IOMMU group\n"); - return PTR_ERR(group); + ret = PTR_ERR(group); + goto error; } ret = iommu_group_add_device(group, dev); @@ -1080,7 +1124,8 @@ static int ipmmu_add_device(struct device *dev) if (ret < 0) { dev_err(dev, "Failed to add device to IPMMU group\n"); - return ret; + group = NULL; + goto error; } archdata = kzalloc(sizeof(*archdata), GFP_KERNEL); @@ -1090,7 +1135,8 @@ static int ipmmu_add_device(struct device *dev) } archdata->mmu = mmu; - archdata->utlb = utlb; + archdata->utlbs = utlbs; + archdata->num_utlbs = num_utlbs; dev->archdata.iommu = archdata; /* @@ -1127,17 +1173,28 @@ static int ipmmu_add_device(struct device *dev) error: arm_iommu_release_mapping(mmu->mapping); + kfree(dev->archdata.iommu); + kfree(utlbs); + dev->archdata.iommu = NULL; - iommu_group_remove_device(dev); + + if (!IS_ERR_OR_NULL(group)) + iommu_group_remove_device(dev); + return ret; } static void ipmmu_remove_device(struct device *dev) { + struct ipmmu_vmsa_archdata *archdata = dev->archdata.iommu; + arm_iommu_detach_device(dev); iommu_group_remove_device(dev); - kfree(dev->archdata.iommu); + + kfree(archdata->utlbs); + kfree(archdata); + dev->archdata.iommu = NULL; }