From patchwork Mon Dec 6 01:58:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Baolu Lu X-Patchwork-Id: 1563796 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=linux-pci-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by bilbo.ozlabs.org (Postfix) with ESMTP id 4J6mny5H4Gz9sRN for ; Mon, 6 Dec 2021 12:59:34 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231923AbhLFCC6 (ORCPT ); Sun, 5 Dec 2021 21:02:58 -0500 Received: from mga06.intel.com ([134.134.136.31]:51453 "EHLO mga06.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231851AbhLFCC4 (ORCPT ); Sun, 5 Dec 2021 21:02:56 -0500 X-IronPort-AV: E=McAfee;i="6200,9189,10189"; a="298027408" X-IronPort-AV: E=Sophos;i="5.87,290,1631602800"; d="scan'208,223";a="298027408" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga104.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 05 Dec 2021 17:59:26 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.87,290,1631602800"; d="scan'208,223";a="514541932" Received: from allen-box.sh.intel.com ([10.239.159.118]) by orsmga008.jf.intel.com with ESMTP; 05 Dec 2021 17:59:19 -0800 From: Lu Baolu To: Greg Kroah-Hartman , Joerg Roedel , Alex Williamson , Bjorn Helgaas , Jason Gunthorpe , Christoph Hellwig , Kevin Tian , Ashok Raj Cc: Will Deacon , Robin Murphy , Dan Williams , rafael@kernel.org, Diana Craciun , Cornelia Huck , Eric Auger , Liu Yi L , Jacob jun Pan , Chaitanya Kulkarni , Stuart Yoder , Laurentiu Tudor , Thierry Reding , David Airlie , Daniel Vetter , Jonathan Hunter , Li Yang , Dmitry Osipenko , iommu@lists.linux-foundation.org, linux-pci@vger.kernel.org, kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Lu Baolu Subject: [PATCH v3 01/18] iommu: Add device dma ownership set/release interfaces Date: Mon, 6 Dec 2021 09:58:46 +0800 Message-Id: <20211206015903.88687-2-baolu.lu@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211206015903.88687-1-baolu.lu@linux.intel.com> References: <20211206015903.88687-1-baolu.lu@linux.intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org From the perspective of who is initiating the device to do DMA, device DMA could be divided into the following types: DMA_OWNER_DMA_API: Device DMAs are initiated by a kernel driver through the kernel DMA API. DMA_OWNER_PRIVATE_DOMAIN: Device DMAs are initiated by a kernel driver with its own PRIVATE domain. DMA_OWNER_PRIVATE_DOMAIN_USER: Device DMAs are initiated by userspace. Different DMA ownerships are exclusive for all devices in the same iommu group as an iommu group is the smallest granularity of device isolation and protection that the IOMMU subsystem can guarantee. This extends the iommu core to enforce this exclusion. Basically two new interfaces are provided: int iommu_device_set_dma_owner(struct device *dev, enum iommu_dma_owner type, void *owner_cookie); void iommu_device_release_dma_owner(struct device *dev, enum iommu_dma_owner type); Although above interfaces are per-device, DMA owner is tracked per group under the hood. An iommu group cannot have different dma ownership set at the same time. Violation of this assumption fails iommu_device_set_dma_owner(). Kernel driver which does DMA have DMA_OWNER_DMA_API automatically set/ released in the driver binding/unbinding process (see next patch). Kernel driver which doesn't do DMA could avoid setting the owner type. Device bound to such driver is considered same as a driver-less device which is compatible to all owner types. Userspace driver framework (e.g. vfio) should set DMA_OWNER_PRIVATE_DOMAIN_USER for a device before the userspace is allowed to access it, plus a owner cookie pointer to mark the user identity so a single group cannot be operated by multiple users simultaneously. Vice versa, the owner type should be released after the user access permission is withdrawn. Signed-off-by: Jason Gunthorpe Signed-off-by: Kevin Tian Signed-off-by: Lu Baolu --- include/linux/iommu.h | 36 +++++++++++++++++ drivers/iommu/iommu.c | 93 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d2f3435e7d17..24676b498f38 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -162,6 +162,23 @@ enum iommu_dev_features { IOMMU_DEV_FEAT_IOPF, }; +/** + * enum iommu_dma_owner - IOMMU DMA ownership + * @DMA_OWNER_NONE: No DMA ownership. + * @DMA_OWNER_DMA_API: Device DMAs are initiated by a kernel driver through + * the kernel DMA API. + * @DMA_OWNER_PRIVATE_DOMAIN: Device DMAs are initiated by a kernel driver + * which provides an UNMANAGED domain. + * @DMA_OWNER_PRIVATE_DOMAIN_USER: Device DMAs are initiated by userspace, + * kernel ensures that DMAs never go to kernel memory. + */ +enum iommu_dma_owner { + DMA_OWNER_NONE, + DMA_OWNER_DMA_API, + DMA_OWNER_PRIVATE_DOMAIN, + DMA_OWNER_PRIVATE_DOMAIN_USER, +}; + #define IOMMU_PASID_INVALID (-1U) #ifdef CONFIG_IOMMU_API @@ -681,6 +698,10 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, void iommu_sva_unbind_device(struct iommu_sva *handle); u32 iommu_sva_get_pasid(struct iommu_sva *handle); +int iommu_device_set_dma_owner(struct device *dev, enum iommu_dma_owner owner, + void *owner_cookie); +void iommu_device_release_dma_owner(struct device *dev, enum iommu_dma_owner owner); + #else /* CONFIG_IOMMU_API */ struct iommu_ops {}; @@ -1081,6 +1102,21 @@ static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev) { return NULL; } + +static inline int iommu_device_set_dma_owner(struct device *dev, + enum iommu_dma_owner owner, + void *owner_cookie) +{ + if (owner != DMA_OWNER_DMA_API) + return -EINVAL; + + return 0; +} + +static inline void iommu_device_release_dma_owner(struct device *dev, + enum iommu_dma_owner owner) +{ +} #endif /* CONFIG_IOMMU_API */ /** diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 8b86406b7162..1de520a07518 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -48,6 +48,9 @@ struct iommu_group { struct iommu_domain *default_domain; struct iommu_domain *domain; struct list_head entry; + enum iommu_dma_owner dma_owner; + refcount_t owner_cnt; + void *owner_cookie; }; struct group_device { @@ -621,6 +624,7 @@ struct iommu_group *iommu_group_alloc(void) INIT_LIST_HEAD(&group->devices); INIT_LIST_HEAD(&group->entry); BLOCKING_INIT_NOTIFIER_HEAD(&group->notifier); + group->dma_owner = DMA_OWNER_NONE; ret = ida_simple_get(&iommu_group_ida, 0, 0, GFP_KERNEL); if (ret < 0) { @@ -3351,3 +3355,92 @@ static ssize_t iommu_group_store_type(struct iommu_group *group, return ret; } + +static int __iommu_group_set_dma_owner(struct iommu_group *group, + enum iommu_dma_owner owner, + void *owner_cookie) +{ + if (refcount_inc_not_zero(&group->owner_cnt)) { + if (group->dma_owner != owner || + group->owner_cookie != owner_cookie) { + refcount_dec(&group->owner_cnt); + return -EBUSY; + } + + return 0; + } + + group->dma_owner = owner; + group->owner_cookie = owner_cookie; + refcount_set(&group->owner_cnt, 1); + + return 0; +} + +static void __iommu_group_release_dma_owner(struct iommu_group *group, + enum iommu_dma_owner owner) +{ + if (WARN_ON(group->dma_owner != owner)) + return; + + if (!refcount_dec_and_test(&group->owner_cnt)) + return; + + group->dma_owner = DMA_OWNER_NONE; +} + +/** + * iommu_device_set_dma_owner() - Set DMA ownership of a device + * @dev: The device. + * @owner: DMA ownership type. + * @owner_cookie: Caller specified pointer. Could be used for exclusive + * declaration. Could be NULL. + * + * Set the DMA ownership of a device. The different ownerships are + * exclusive. The caller could specify a owner_cookie pointer so that + * the same DMA ownership could be exclusive among different owners. + */ +int iommu_device_set_dma_owner(struct device *dev, enum iommu_dma_owner owner, + void *owner_cookie) +{ + struct iommu_group *group = iommu_group_get(dev); + int ret; + + if (!group) { + if (owner == DMA_OWNER_DMA_API) + return 0; + else + return -ENODEV; + } + + mutex_lock(&group->mutex); + ret = __iommu_group_set_dma_owner(group, owner, owner_cookie); + mutex_unlock(&group->mutex); + iommu_group_put(group); + + return ret; +} +EXPORT_SYMBOL_GPL(iommu_device_set_dma_owner); + +/** + * iommu_device_release_dma_owner() - Release DMA ownership of a device + * @dev: The device. + * @owner: The DMA ownership type. + * + * Release the DMA ownership claimed by iommu_device_set_dma_owner(). + */ +void iommu_device_release_dma_owner(struct device *dev, enum iommu_dma_owner owner) +{ + struct iommu_group *group = iommu_group_get(dev); + + if (!group) { + WARN_ON(owner != DMA_OWNER_DMA_API); + return; + } + + mutex_lock(&group->mutex); + __iommu_group_release_dma_owner(group, owner); + mutex_unlock(&group->mutex); + iommu_group_put(group); +} +EXPORT_SYMBOL_GPL(iommu_device_release_dma_owner);