Message ID | 12-v5-4001c2997bd0+30c-iommufd_jgg@nvidia.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | IOMMUFD Generic interface | expand |
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
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 = {