From patchwork Wed May 4 11:40:04 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Auger X-Patchwork-Id: 9012611 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id E88F99F1D3 for ; Wed, 4 May 2016 11:43:13 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C4F9E2039C for ; Wed, 4 May 2016 11:43:12 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4F832203AA for ; Wed, 4 May 2016 11:43:11 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1axvBS-0002Ij-UK; Wed, 04 May 2016 11:41:38 +0000 Received: from mail-wm0-x236.google.com ([2a00:1450:400c:c09::236]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1axvAc-0001VV-W0 for linux-arm-kernel@lists.infradead.org; Wed, 04 May 2016 11:40:49 +0000 Received: by mail-wm0-x236.google.com with SMTP id g17so88775549wme.1 for ; Wed, 04 May 2016 04:40:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=lDjqT923GN6NCNe3wkgjSBJmUyutWW9gm8U0h1p3Ioo=; b=UqNbl+HRFYYNYqQoeIq7EFvcIx3zWFDiBKkflfQS/Xn5DKDO5fK+qF02XuYXiK9CFb BPXIydIstgPdF/nNSODwUNCTjXcaJ1FyXy//QCXoIT1h/a0eyo7R+c2HmGl9YcL7mmRA HiZ+gPpRqZQwbWAxGn1GP0ZbP1mTeiUh9c2UE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=lDjqT923GN6NCNe3wkgjSBJmUyutWW9gm8U0h1p3Ioo=; b=KEZ80eMLxuCtM5i74Ra9bLH2h3eG6oHEBcDxtwcnkXO8T6SxJq+9CbNQH3Z8vfPITh aLplFozm6FdH451A86QsTOfyQ0BULaQgUhdCFugNit/h3w9ekopIJ+Z1RGwfMOB+QTWz tLSyFdOvFW0S9d2C28A3r5CzyDQ75JMnaNTymh4yFzr5PCXqN9HayJqqubxDW0liMLDr c27wdl0uIWBXU0bti/QKL7BA/Jy3ZlUeUkyv6316GTel0qmCKVhefZ776OvY7Fro1fzm 1DaR3LUX1pcduRbN1V4f7gSuzkPAVG6EGiqppsoPGSLWOdNWz4YzgA/SGXd6iwbK7Zqa TLpw== X-Gm-Message-State: AOPr4FVvbCmCSjoOyTYu7Nw53AkDMf/WirZZS301Jkh3K6LhIXS8Mor+Jy29+53nhwCniDlj X-Received: by 10.194.77.140 with SMTP id s12mr8253987wjw.24.1462362025398; Wed, 04 May 2016 04:40:25 -0700 (PDT) Received: from new-host-61.home (LMontsouris-657-1-37-90.w80-11.abo.wanadoo.fr. [80.11.198.90]) by smtp.gmail.com with ESMTPSA id u6sm3766963wjh.2.2016.05.04.04.40.23 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 04 May 2016 04:40:24 -0700 (PDT) From: Eric Auger To: eric.auger@st.com, eric.auger@linaro.org, robin.murphy@arm.com, alex.williamson@redhat.com, will.deacon@arm.com, joro@8bytes.org, tglx@linutronix.de, jason@lakedaemon.net, marc.zyngier@arm.com, christoffer.dall@linaro.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH v9 5/8] iommu/msi-iommu: iommu_msi_[get,put]_doorbell_iova Date: Wed, 4 May 2016 11:40:04 +0000 Message-Id: <1462362007-2753-6-git-send-email-eric.auger@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1462362007-2753-1-git-send-email-eric.auger@linaro.org> References: <1462362007-2753-1-git-send-email-eric.auger@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160504_044047_410871_4614070F X-CRM114-Status: GOOD ( 19.68 ) X-Spam-Score: -2.7 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: julien.grall@arm.com, patches@linaro.org, Jean-Philippe.Brucker@arm.com, p.fedin@samsung.com, linux-kernel@vger.kernel.org, Bharat.Bhushan@freescale.com, iommu@lists.linux-foundation.org, pranav.sawargaonkar@gmail.com, yehuday@marvell.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-6.2 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=unavailable 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 iommu_msi_get_doorbell_iova allows to iommu map an MSI doorbell contiguous physical region onto a reserved contiguous IOVA region. The physical region base address does not need to be iommu page size aligned. iova pages are allocated and mapped so that they cover all the physical region. This mapping is tracked as a whole (and cannot be split). In case a mapping already exists for the physical pages, the IOVA mapped to the PA base is directly returned. Each time the get succeeds a binding ref count is incremented. iommu_put_reserved_iova decrements the ref count and when this latter is null, the mapping is destroyed and the IOVAs are released. Signed-off-by: Eric Auger --- v9: - use iommu_domain_msi_aperture_valid v8: - function renaming - new design based on the assumption the iova domain cannot disappear - free_iova outside of doorbell_mapping_release (iommu_domain * was removed from doorbell_mapping) v7: - change title and rework commit message with new name of the functions and size parameter - fix locking - rework header doc comments - put now takes a phys_addr_t - check prot argument against reserved_iova_domain prot flags v5 -> v6: - revisit locking with spin_lock instead of mutex - do not kref_get on 1st get - add size parameter to the get function following Marc's request - use the iova domain shift instead of using the smallest supported page siz v3 -> v4: - formerly in iommu: iommu_get/put_single_reserved & iommu/arm-smmu: implement iommu_get/put_single_reserved - Attempted to address Marc's doubts about missing size/alignment at VFIO level (user-space knows the IOMMU page size and the number of IOVA pages to provision) v2 -> v3: - remove static implementation of iommu_get_single_reserved & iommu_put_single_reserved when CONFIG_IOMMU_API is not set v1 -> v2: - previously a VFIO API, named vfio_alloc_map/unmap_free_reserved_iova --- drivers/iommu/msi-iommu.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/msi-iommu.h | 39 ++++++++++++ 2 files changed, 189 insertions(+) diff --git a/drivers/iommu/msi-iommu.c b/drivers/iommu/msi-iommu.c index 4292957..ec97f49 100644 --- a/drivers/iommu/msi-iommu.c +++ b/drivers/iommu/msi-iommu.c @@ -95,3 +95,153 @@ int iommu_msi_set_aperture(struct iommu_domain *domain, } EXPORT_SYMBOL_GPL(iommu_msi_set_aperture); +/* called with info->lock held */ +static struct doorbell_mapping * +search_msi_doorbell_mapping(struct doorbell_mapping_info *info, + phys_addr_t addr, size_t size) +{ + struct doorbell_mapping *mapping; + + list_for_each_entry(mapping, &info->list, next) { + if ((addr >= mapping->addr) && + (addr + size <= mapping->addr + mapping->size)) + return mapping; + } + return NULL; +} + +int iommu_msi_get_doorbell_iova(struct iommu_domain *domain, + phys_addr_t addr, size_t size, int prot, + dma_addr_t *iova) +{ + struct doorbell_mapping_info *dmi = domain->msi_cookie; + struct iova_domain *iovad = domain->iova_cookie; + struct doorbell_mapping *new_mapping, *mapping; + phys_addr_t aligned_base, offset; + size_t binding_size; + struct iova *p_iova; + dma_addr_t new_iova; + int ret = -EINVAL; + bool unmap = false; + + if (!dmi) + return -ENODEV; + + if (!iommu_domain_msi_aperture_valid(domain)) + return -EINVAL; + + offset = iova_offset(iovad, addr); + aligned_base = addr - offset; + binding_size = iova_align(iovad, size + offset); + + spin_lock(&dmi->lock); + + mapping = search_msi_doorbell_mapping(dmi, aligned_base, binding_size); + if (mapping) { + *iova = mapping->iova + offset + aligned_base - mapping->addr; + kref_get(&mapping->kref); + ret = 0; + goto unlock; + } + + spin_unlock(&dmi->lock); + + new_mapping = kzalloc(sizeof(*new_mapping), GFP_KERNEL); + if (!new_mapping) + return -ENOMEM; + + p_iova = alloc_iova(iovad, binding_size >> iova_shift(iovad), + iovad->dma_32bit_pfn, true); + if (!p_iova) { + kfree(new_mapping); + return -ENOMEM; + } + + new_iova = iova_dma_addr(iovad, p_iova); + *iova = new_iova; + + /* iommu_map is not supposed to be atomic */ + ret = iommu_map(domain, *iova, aligned_base, binding_size, prot); + + spin_lock(&dmi->lock); + + if (ret) + goto free_iova; + /* + * check again the doorbell mapping was not added while the lock + * was released + */ + mapping = search_msi_doorbell_mapping(dmi, aligned_base, binding_size); + if (mapping) { + *iova = mapping->iova + offset + aligned_base - mapping->addr; + kref_get(&mapping->kref); + ret = 0; + unmap = true; + goto free_iova; + } + + kref_init(&new_mapping->kref); + new_mapping->addr = aligned_base; + new_mapping->iova = *iova; + new_mapping->size = binding_size; + + list_add(&new_mapping->next, &dmi->list); + + *iova += offset; + goto unlock; +free_iova: + free_iova(iovad, p_iova->pfn_lo); + kfree(new_mapping); +unlock: + spin_unlock(&dmi->lock); + if (unmap) + iommu_unmap(domain, new_iova, binding_size); + return ret; +} +EXPORT_SYMBOL_GPL(iommu_msi_get_doorbell_iova); + +static void doorbell_mapping_release(struct kref *kref) +{ + struct doorbell_mapping *mapping = + container_of(kref, struct doorbell_mapping, kref); + + list_del(&mapping->next); + kfree(mapping); +} + +void iommu_msi_put_doorbell_iova(struct iommu_domain *domain, phys_addr_t addr) +{ + struct doorbell_mapping_info *dmi = domain->msi_cookie; + struct iova_domain *iovad = domain->iova_cookie; + phys_addr_t aligned_addr, page_size, offset; + struct doorbell_mapping *mapping; + dma_addr_t iova; + size_t size; + int ret = 0; + + if (!dmi) + return; + + page_size = (uint64_t)1 << iova_shift(iovad); + offset = iova_offset(iovad, addr); + aligned_addr = addr - offset; + + spin_lock(&dmi->lock); + + mapping = search_msi_doorbell_mapping(dmi, aligned_addr, page_size); + if (!mapping) + goto unlock; + + iova = mapping->iova; + size = mapping->size; + + ret = kref_put(&mapping->kref, doorbell_mapping_release); + +unlock: + spin_unlock(&dmi->lock); + if (ret) { + iommu_unmap(domain, iova, size); + free_iova(iovad, iova_pfn(iovad, iova)); + } +} +EXPORT_SYMBOL_GPL(iommu_msi_put_doorbell_iova); diff --git a/include/linux/msi-iommu.h b/include/linux/msi-iommu.h index 392aa6f..1cd115f 100644 --- a/include/linux/msi-iommu.h +++ b/include/linux/msi-iommu.h @@ -52,6 +52,35 @@ void iommu_put_msi_cookie(struct iommu_domain *domain); int iommu_msi_set_aperture(struct iommu_domain *domain, dma_addr_t start, dma_addr_t end); +/** + * iommu_msi_get_doorbell_iova: allocate a contiguous set of iova pages and + * map them to the MSI doorbell's physical range defined by @addr and @size. + * + * @domain: iommu domain handle + * @addr: physical address to bind + * @size: size of the binding + * @prot: mapping protection attribute + * @iova: returned iova + * + * Mapped physical pfns are within [@addr >> order, (@addr + size -1) >> order] + * where order corresponds to the iova domain order. + * This mapping is tracked and reference counted with the minimal granularity + * of @size. + */ +int iommu_msi_get_doorbell_iova(struct iommu_domain *domain, + phys_addr_t addr, size_t size, int prot, + dma_addr_t *iova); + +/** + * iommu_msi_put_doorbell_iova: decrement a ref count of the doorbell's mapping + * + * @domain: iommu domain handle + * @addr: physical address whose binding ref count is decremented + * + * if the binding ref count is null, destroy the MSI doorbell's mapping + */ +void iommu_msi_put_doorbell_iova(struct iommu_domain *domain, phys_addr_t addr); + #else static inline int @@ -61,5 +90,15 @@ iommu_msi_set_aperture(struct iommu_domain *domain, return -ENOENT; } +static inline int iommu_msi_get_doorbell_iova(struct iommu_domain *domain, + phys_addr_t addr, size_t size, + int prot, dma_addr_t *iova) +{ + return -ENOENT; +} + +static inline void iommu_msi_put_doorbell_iova(struct iommu_domain *domain, + phys_addr_t addr) {} + #endif /* CONFIG_IOMMU_MSI */ #endif /* __MSI_IOMMU_H */