Re: [PATCH v5 12/19] iommufd: Add a HW pagetable object

Eric Auger
2022-11-27T15:12:22Z

Hi Jason,

On 11/16/22 22:00, Jason Gunthorpe wrote:
> The hw_pagetable object exposes the internal struct iommu_domain's to
> userspace. An iommu_domain is required when any DMA device attaches to an
> IOAS to control the io page table through the iommu driver.
>
> For compatibility with VFIO the hw_pagetable is automatically created when
> a DMA device is attached to the IOAS. If a compatible iommu_domain already
> exists then the hw_pagetable associated with it is used for the
> attachment.
>
> In the initial series there is no iommufd uAPI for the hw_pagetable
> object. The next patch provides driver facing APIs for IO page table
> attachment that allows drivers to accept either an IOAS or a hw_pagetable
> ID and for the driver to return the hw_pagetable ID that was auto-selected
> from an IOAS. The expectation is the driver will provide uAPI through its
> own FD for attaching its device to iommufd. This allows userspace to learn
> the mapping of devices to iommu_domains and to override the automatic
> attachment.
>
> The future HW specific interface will allow userspace to create
> hw_pagetable objects using iommu_domains with IOMMU driver specific
> parameters. This infrastructure will allow linking those domains to IOAS's
> and devices.
>
> Tested-by: Nicolin Chen <nicolinc@nvidia.com>
> Tested-by: Yi Liu <yi.l.liu@intel.com>
> Tested-by: Lixiao Yang <lixiao.yang@intel.com>
> Tested-by: Matthew Rosato <mjrosato@linux.ibm.com>
> Reviewed-by: Kevin Tian <kevin.tian@intel.com>
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
> ---
> drivers/iommu/iommufd/Makefile | 1 +
> drivers/iommu/iommufd/hw_pagetable.c | 57 +++++++++++++++++++++++++
> drivers/iommu/iommufd/ioas.c | 3 ++
> drivers/iommu/iommufd/iommufd_private.h | 33 ++++++++++++++
> drivers/iommu/iommufd/main.c | 3 ++
> 5 files changed, 97 insertions(+)
> create mode 100644 drivers/iommu/iommufd/hw_pagetable.c
>
> diff --git a/drivers/iommu/iommufd/Makefile b/drivers/iommu/iommufd/Makefile
> index 2b4f36f1b72f9d..e13e971aa28c60 100644
> --- a/drivers/iommu/iommufd/Makefile
> +++ b/drivers/iommu/iommufd/Makefile
> @@ -1,5 +1,6 @@
> # SPDX-License-Identifier: GPL-2.0-only
> iommufd-y := \
> + hw_pagetable.o \
> io_pagetable.o \
> ioas.o \
> main.o \
> diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
> new file mode 100644
> index 00000000000000..43d473989a0667
> --- /dev/null
> +++ b/drivers/iommu/iommufd/hw_pagetable.c
> @@ -0,0 +1,57 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
> + */
> +#include <linux/iommu.h>
> +
> +#include "iommufd_private.h"
> +
> +void iommufd_hw_pagetable_destroy(struct iommufd_object *obj)
> +{
> + struct iommufd_hw_pagetable *hwpt =
> + container_of(obj, struct iommufd_hw_pagetable, obj);
> +
> + WARN_ON(!list_empty(&hwpt->devices));
> +
> + iommu_domain_free(hwpt->domain);
> + refcount_dec(&hwpt->ioas->obj.users);
> + mutex_destroy(&hwpt->devices_lock);
> +}
> +
> +/**
> + * iommufd_hw_pagetable_alloc() - Get an iommu_domain for a device
> + * @ictx: iommufd context
> + * @ioas: IOAS to associate the domain with
> + * @dev: Device to get an iommu_domain for
> + *
> + * Allocate a new iommu_domain and return it as a hw_pagetable.
> + */
> +struct iommufd_hw_pagetable *
> +iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
> + struct device *dev)
> +{
> + struct iommufd_hw_pagetable *hwpt;
> + int rc;
> +
> + hwpt = iommufd_object_alloc(ictx, hwpt, IOMMUFD_OBJ_HW_PAGETABLE);
> + if (IS_ERR(hwpt))
> + return hwpt;
> +
> + hwpt->domain = iommu_domain_alloc(dev->bus);
> + if (!hwpt->domain) {
> + rc = -ENOMEM;
> + goto out_abort;
> + }
> +
> + INIT_LIST_HEAD(&hwpt->devices);
> + INIT_LIST_HEAD(&hwpt->hwpt_item);
> + mutex_init(&hwpt->devices_lock);
> + /* Pairs with iommufd_hw_pagetable_destroy() */
> + refcount_inc(&ioas->obj.users);
> + hwpt->ioas = ioas;
> + return hwpt;
> +
> +out_abort:
> + iommufd_object_abort(ictx, &hwpt->obj);
> + return ERR_PTR(rc);
> +}
> diff --git a/drivers/iommu/iommufd/ioas.c b/drivers/iommu/iommufd/ioas.c
> index 7671456e86413a..64e6d0f73e39aa 100644
> --- a/drivers/iommu/iommufd/ioas.c
> +++ b/drivers/iommu/iommufd/ioas.c
> @@ -17,6 +17,7 @@ void iommufd_ioas_destroy(struct iommufd_object *obj)
> rc = iopt_unmap_all(&ioas->iopt, NULL);
> WARN_ON(rc && rc != -ENOENT);
> iopt_destroy_table(&ioas->iopt);
> + mutex_destroy(&ioas->mutex);
> }
>
> struct iommufd_ioas *iommufd_ioas_alloc(struct iommufd_ctx *ictx)
> @@ -28,6 +29,8 @@ struct iommufd_ioas *iommufd_ioas_alloc(struct iommufd_ctx *ictx)
> return ioas;
>
> iopt_init_table(&ioas->iopt);
> + INIT_LIST_HEAD(&ioas->hwpt_list);
> + mutex_init(&ioas->mutex);
> return ioas;
> }
>
> diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
> index 6721332dbbba03..bb5cbd8f4e5991 100644
> --- a/drivers/iommu/iommufd/iommufd_private.h
> +++ b/drivers/iommu/iommufd/iommufd_private.h
> @@ -103,6 +103,7 @@ static inline int iommufd_ucmd_respond(struct iommufd_ucmd *ucmd,
> enum iommufd_object_type {
> IOMMUFD_OBJ_NONE,
> IOMMUFD_OBJ_ANY = IOMMUFD_OBJ_NONE,
> + IOMMUFD_OBJ_HW_PAGETABLE,
> IOMMUFD_OBJ_IOAS,
> };
>
> @@ -181,10 +182,20 @@ struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx,
> * io_pagetable object. It is a user controlled mapping of IOVA -> PFNs. The
> * mapping is copied into all of the associated domains and made available to
> * in-kernel users.
> + *
> + * Every iommu_domain that is created is wrapped in a iommufd_hw_pagetable
> + * object. When we go to attach a device to an IOAS we need to get an
> + * iommu_domain and wrapping iommufd_hw_pagetable for it.
> + *
> + * An iommu_domain & iommfd_hw_pagetable will be automatically selected
> + * for a device based on the hwpt_list. If no suitable iommu_domain
> + * is found a new iommu_domain will be created.
> */
> struct iommufd_ioas {
> struct iommufd_object obj;
> struct io_pagetable iopt;
> + struct mutex mutex;+ struct list_head hwpt_list;
> };
>
> static inline struct iommufd_ioas *iommufd_get_ioas(struct iommufd_ucmd *ucmd,
> @@ -207,6 +218,28 @@ int iommufd_ioas_option(struct iommufd_ucmd *ucmd);
> int iommufd_option_rlimit_mode(struct iommu_option *cmd,
> struct iommufd_ctx *ictx);
>
> +/*
> + * A HW pagetable is called an iommu_domain inside the kernel. This user object
> + * allows directly creating and inspecting the domains. Domains that have kernel
> + * owned page tables will be associated with an iommufd_ioas that provides the
> + * IOVA to PFN map.
> + */
> +struct iommufd_hw_pagetable {
> + struct iommufd_object obj;
> + struct iommufd_ioas *ioas;
> + struct iommu_domain *domain;
> + bool auto_domain : 1;
> + /* Head at iommufd_ioas::hwpt_list */
> + struct list_head hwpt_item;
> + struct mutex devices_lock;
> + struct list_head devices;
> +};
> +
> +struct iommufd_hw_pagetable *
> +iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
> + struct device *dev);
> +void iommufd_hw_pagetable_destroy(struct iommufd_object *obj);
> +
> struct iommufd_access {
> unsigned long iova_alignment;
> u32 iopt_access_list_id;
> diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c
> index 266109045537ed..3eab714b8e12a3 100644
> --- a/drivers/iommu/iommufd/main.c
> +++ b/drivers/iommu/iommufd/main.c
> @@ -355,6 +355,9 @@ static const struct iommufd_object_ops iommufd_object_ops[] = {
> [IOMMUFD_OBJ_IOAS] = {
> .destroy = iommufd_ioas_destroy,
> },
> + [IOMMUFD_OBJ_HW_PAGETABLE] = {
> + .destroy = iommufd_hw_pagetable_destroy,
> + },
> };
>
> static struct miscdevice iommu_misc_dev = {

Reviewed-by: Eric Auger <eric.auger@redhat.com>

Eric