diff mbox series

[v5,06/13] iommufd: Allow pt_id to carry viommu_id for IOMMU_HWPT_ALLOC

Message ID 4f5ed9d75c23a503ffe672a85cf9010569652794.1729897352.git.nicolinc@nvidia.com (mailing list archive)
State New
Headers show
Series iommufd: Add vIOMMU infrastructure (Part-1) | expand

Commit Message

Nicolin Chen Oct. 25, 2024, 11:49 p.m. UTC
Now a vIOMMU holds a shareable nesting parent HWPT. So, it can act like
that nesting parent HWPT to allocate a nested HWPT.

Support that in the IOMMU_HWPT_ALLOC ioctl handler, and update its kdoc.

Also, add an iommufd_hwpt_nested_alloc_for_viommu helper to allocate a
nested HWPT for a vIOMMU object. Since a vIOMMU object holds the parent
hwpt's refcount already, increase the refcount of the vIOMMU only.

Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
---
 drivers/iommu/iommufd/iommufd_private.h |  1 +
 include/uapi/linux/iommufd.h            | 14 ++---
 drivers/iommu/iommufd/hw_pagetable.c    | 71 ++++++++++++++++++++++++-
 3 files changed, 79 insertions(+), 7 deletions(-)

Comments

Tian, Kevin Oct. 28, 2024, 2:46 a.m. UTC | #1
> From: Nicolin Chen <nicolinc@nvidia.com>
> Sent: Saturday, October 26, 2024 7:50 AM
> 
> Now a vIOMMU holds a shareable nesting parent HWPT. So, it can act like
> that nesting parent HWPT to allocate a nested HWPT.
> 
> Support that in the IOMMU_HWPT_ALLOC ioctl handler, and update its kdoc.
> 
> Also, add an iommufd_hwpt_nested_alloc_for_viommu helper to allocate a

the helper name is not updated.

> nested HWPT for a vIOMMU object. Since a vIOMMU object holds the parent
> hwpt's refcount already, increase the refcount of the vIOMMU only.
> 
> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>

Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Zhangfei Gao Oct. 28, 2024, 3:24 a.m. UTC | #2
On Sat, 26 Oct 2024 at 07:50, Nicolin Chen <nicolinc@nvidia.com> wrote:
>
> Now a vIOMMU holds a shareable nesting parent HWPT. So, it can act like
> that nesting parent HWPT to allocate a nested HWPT.
>
> Support that in the IOMMU_HWPT_ALLOC ioctl handler, and update its kdoc.
>
> Also, add an iommufd_hwpt_nested_alloc_for_viommu helper to allocate a
> nested HWPT for a vIOMMU object. Since a vIOMMU object holds the parent
> hwpt's refcount already, increase the refcount of the vIOMMU only.
>
> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
> ---
>  drivers/iommu/iommufd/iommufd_private.h |  1 +
>  include/uapi/linux/iommufd.h            | 14 ++---
>  drivers/iommu/iommufd/hw_pagetable.c    | 71 ++++++++++++++++++++++++-
>  3 files changed, 79 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
> index 9adf8d616796..8c9ab35eaea5 100644
> --- a/drivers/iommu/iommufd/iommufd_private.h
> +++ b/drivers/iommu/iommufd/iommufd_private.h
> @@ -288,6 +288,7 @@ struct iommufd_hwpt_paging {
>  struct iommufd_hwpt_nested {
>         struct iommufd_hw_pagetable common;
>         struct iommufd_hwpt_paging *parent;
> +       struct iommufd_viommu *viommu;
>  };
>
>  static inline bool hwpt_is_paging(struct iommufd_hw_pagetable *hwpt)
> diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
> index 3d320d069654..717659b9fdce 100644
> --- a/include/uapi/linux/iommufd.h
> +++ b/include/uapi/linux/iommufd.h
> @@ -430,7 +430,7 @@ enum iommu_hwpt_data_type {
>   * @size: sizeof(struct iommu_hwpt_alloc)
>   * @flags: Combination of enum iommufd_hwpt_alloc_flags
>   * @dev_id: The device to allocate this HWPT for
> - * @pt_id: The IOAS or HWPT to connect this HWPT to
> + * @pt_id: The IOAS or HWPT or vIOMMU to connect this HWPT to
>   * @out_hwpt_id: The ID of the new HWPT
>   * @__reserved: Must be 0
>   * @data_type: One of enum iommu_hwpt_data_type
> @@ -449,11 +449,13 @@ enum iommu_hwpt_data_type {
>   * IOMMU_HWPT_DATA_NONE. The HWPT can be allocated as a parent HWPT for a
>   * nesting configuration by passing IOMMU_HWPT_ALLOC_NEST_PARENT via @flags.
>   *
> - * A user-managed nested HWPT will be created from a given parent HWPT via
> - * @pt_id, in which the parent HWPT must be allocated previously via the
> - * same ioctl from a given IOAS (@pt_id). In this case, the @data_type
> - * must be set to a pre-defined type corresponding to an I/O page table
> - * type supported by the underlying IOMMU hardware.
> + * A user-managed nested HWPT will be created from a given vIOMMU (wrapping a
> + * parent HWPT) or a parent HWPT via @pt_id, in which the parent HWPT must be
> + * allocated previously via the same ioctl from a given IOAS (@pt_id). In this
> + * case, the @data_type must be set to a pre-defined type corresponding to an
> + * I/O page table type supported by the underlying IOMMU hardware. The device
> + * via @dev_id and the vIOMMU via @pt_id must be associated to the same IOMMU
> + * instance.
>   *
>   * If the @data_type is set to IOMMU_HWPT_DATA_NONE, @data_len and
>   * @data_uptr should be zero. Otherwise, both @data_len and @data_uptr
> diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
> index d06bf6e6c19f..1df5d40c93df 100644
> --- a/drivers/iommu/iommufd/hw_pagetable.c
> +++ b/drivers/iommu/iommufd/hw_pagetable.c
> @@ -57,7 +57,10 @@ void iommufd_hwpt_nested_destroy(struct iommufd_object *obj)
>                 container_of(obj, struct iommufd_hwpt_nested, common.obj);
>
>         __iommufd_hwpt_destroy(&hwpt_nested->common);
> -       refcount_dec(&hwpt_nested->parent->common.obj.users);
> +       if (hwpt_nested->viommu)
> +               refcount_dec(&hwpt_nested->viommu->obj.users);
> +       else
> +               refcount_dec(&hwpt_nested->parent->common.obj.users);
>  }
>
>  void iommufd_hwpt_nested_abort(struct iommufd_object *obj)
> @@ -260,6 +263,56 @@ iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx,
>         return ERR_PTR(rc);
>  }
>
> +/**
> + * iommufd_viommu_alloc_hwpt_nested() - Get a hwpt_nested for a vIOMMU
> + * @viommu: vIOMMU ojbect to associate the hwpt_nested/domain with
> + * @user_data: user_data pointer. Must be valid
> + *
> + * Allocate a new IOMMU_DOMAIN_NESTED for a vIOMMU and return it as a NESTED
> + * hw_pagetable.
> + */
> +static struct iommufd_hwpt_nested *
> +iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
> +                                const struct iommu_user_data *user_data)
> +{
> +       struct iommufd_hwpt_nested *hwpt_nested;
> +       struct iommufd_hw_pagetable *hwpt;
> +       int rc;
> +
> +       if (flags)
> +               return ERR_PTR(-EOPNOTSUPP);

This check should be removed.

When a user page fault is required, IOMMU_HWPT_FAULT_ID_VALID is set.
if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {

By the way, has qemu changed compared with v3?
I still got a hardware error in this version, in check

Thanks
Jason Gunthorpe Oct. 28, 2024, 1:03 p.m. UTC | #3
On Mon, Oct 28, 2024 at 11:24:10AM +0800, Zhangfei Gao wrote:

> > +/**
> > + * iommufd_viommu_alloc_hwpt_nested() - Get a hwpt_nested for a vIOMMU
> > + * @viommu: vIOMMU ojbect to associate the hwpt_nested/domain with
> > + * @user_data: user_data pointer. Must be valid
> > + *
> > + * Allocate a new IOMMU_DOMAIN_NESTED for a vIOMMU and return it as a NESTED
> > + * hw_pagetable.
> > + */
> > +static struct iommufd_hwpt_nested *
> > +iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
> > +                                const struct iommu_user_data *user_data)
> > +{
> > +       struct iommufd_hwpt_nested *hwpt_nested;
> > +       struct iommufd_hw_pagetable *hwpt;
> > +       int rc;
> > +
> > +       if (flags)
> > +               return ERR_PTR(-EOPNOTSUPP);
> 
> This check should be removed.
> 
> When a user page fault is required, IOMMU_HWPT_FAULT_ID_VALID is set.
> if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {

It can't just be removed..

I suspect that IOMMU_HWPT_FAULT_ID_VALID should not be set on the
nested domain but on the parent?

Jason
Nicolin Chen Oct. 28, 2024, 2:52 p.m. UTC | #4
On Mon, Oct 28, 2024 at 10:03:09AM -0300, Jason Gunthorpe wrote:
> On Mon, Oct 28, 2024 at 11:24:10AM +0800, Zhangfei Gao wrote:
> 
> > > +/**
> > > + * iommufd_viommu_alloc_hwpt_nested() - Get a hwpt_nested for a vIOMMU
> > > + * @viommu: vIOMMU ojbect to associate the hwpt_nested/domain with
> > > + * @user_data: user_data pointer. Must be valid
> > > + *
> > > + * Allocate a new IOMMU_DOMAIN_NESTED for a vIOMMU and return it as a NESTED
> > > + * hw_pagetable.
> > > + */
> > > +static struct iommufd_hwpt_nested *
> > > +iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
> > > +                                const struct iommu_user_data *user_data)
> > > +{
> > > +       struct iommufd_hwpt_nested *hwpt_nested;
> > > +       struct iommufd_hw_pagetable *hwpt;
> > > +       int rc;
> > > +
> > > +       if (flags)
> > > +               return ERR_PTR(-EOPNOTSUPP);
> > 
> > This check should be removed.
> > 
> > When a user page fault is required, IOMMU_HWPT_FAULT_ID_VALID is set.
> > if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {
> 
> It can't just be removed..
> 
> I suspect that IOMMU_HWPT_FAULT_ID_VALID should not be set on the
> nested domain but on the parent?

By giving another look,

In iommufd_hwpt_paging_alloc(), we reject IOMMU_HWPT_FAULT_ID_VALID:
	const u32 valid_flags = IOMMU_HWPT_ALLOC_NEST_PARENT |
				IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
	...
	if (flags & ~valid_flags)
		return ERR_PTR(-EOPNOTSUPP);

In iommufd_hwpt_nested_alloc(), we mask the flag away:
	if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) ||
	    !user_data->len || !ops->domain_alloc_user)
		return ERR_PTR(-EOPNOTSUPP);
	...
	hwpt->domain = ops->domain_alloc_user(idev->dev,
					      flags & ~IOMMU_HWPT_FAULT_ID_VALID,
					      parent->common.domain, user_data);

Then, in the common function it has a section of
	if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {
	...

It seems that this IOMMU_HWPT_FAULT_ID_VALID is for nested domains?

So, aligning with that, here we need:
	if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) || !user_data->len)
		return ERR_PTR(-EOPNOTSUPP);

And looks like we don't have some detailed documentation w.r.t.
this flag and Fault Queue. We'd likely need to update the uAPI
doc, prior to (or along with) the Part-3 vIRQ series.

Thanks
Nicolin
Zhangfei Gao Oct. 28, 2024, 2:53 p.m. UTC | #5
On Mon, 28 Oct 2024 at 11:24, Zhangfei Gao <zhangfei.gao@linaro.org> wrote:

> By the way, has qemu changed compared with v3?
> I still got a hardware error in this version, in check

Found iommufd_viommu_p2-v5 misses some patches,
Simply tested ok with iommufd_viommu_p2-v5-with-rmr, (with some hacks)

Thanks
Nicolin Chen Oct. 28, 2024, 3:01 p.m. UTC | #6
On Mon, Oct 28, 2024 at 10:53:38PM +0800, Zhangfei Gao wrote:
> On Mon, 28 Oct 2024 at 11:24, Zhangfei Gao <zhangfei.gao@linaro.org> wrote:
> 
> > By the way, has qemu changed compared with v3?
> > I still got a hardware error in this version, in check
> 
> Found iommufd_viommu_p2-v5 misses some patches,
> Simply tested ok with iommufd_viommu_p2-v5-with-rmr, (with some hacks)

I see. I put those RMR patches on top of Part-1&2 in v5, v.s.
Part-1&2 rebasing on RMR patches.

Thanks for testing!

Nicolin
Nicolin Chen Oct. 28, 2024, 9:08 p.m. UTC | #7
On Mon, Oct 28, 2024 at 07:52:10AM -0700, Nicolin Chen wrote:
> On Mon, Oct 28, 2024 at 10:03:09AM -0300, Jason Gunthorpe wrote:
> > On Mon, Oct 28, 2024 at 11:24:10AM +0800, Zhangfei Gao wrote:
> >
> > > > +/**
> > > > + * iommufd_viommu_alloc_hwpt_nested() - Get a hwpt_nested for a vIOMMU
> > > > + * @viommu: vIOMMU ojbect to associate the hwpt_nested/domain with
> > > > + * @user_data: user_data pointer. Must be valid
> > > > + *
> > > > + * Allocate a new IOMMU_DOMAIN_NESTED for a vIOMMU and return it as a NESTED
> > > > + * hw_pagetable.
> > > > + */
> > > > +static struct iommufd_hwpt_nested *
> > > > +iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
> > > > +                                const struct iommu_user_data *user_data)
> > > > +{
> > > > +       struct iommufd_hwpt_nested *hwpt_nested;
> > > > +       struct iommufd_hw_pagetable *hwpt;
> > > > +       int rc;
> > > > +
> > > > +       if (flags)
> > > > +               return ERR_PTR(-EOPNOTSUPP);
> > >
> > > This check should be removed.
> > >
> > > When a user page fault is required, IOMMU_HWPT_FAULT_ID_VALID is set.
> > > if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {
> >
> > It can't just be removed..
> >
> > I suspect that IOMMU_HWPT_FAULT_ID_VALID should not be set on the
> > nested domain but on the parent?
> 
> By giving another look,
> 
> In iommufd_hwpt_paging_alloc(), we reject IOMMU_HWPT_FAULT_ID_VALID:
>         const u32 valid_flags = IOMMU_HWPT_ALLOC_NEST_PARENT |
>                                 IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
>         ...
>         if (flags & ~valid_flags)
>                 return ERR_PTR(-EOPNOTSUPP);
> 
> In iommufd_hwpt_nested_alloc(), we mask the flag away:
>         if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) ||
>             !user_data->len || !ops->domain_alloc_user)
>                 return ERR_PTR(-EOPNOTSUPP);
>         ...
>         hwpt->domain = ops->domain_alloc_user(idev->dev,
>                                               flags & ~IOMMU_HWPT_FAULT_ID_VALID,
>                                               parent->common.domain, user_data);
> 
> Then, in the common function it has a section of
>         if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {
>         ...
> 
> It seems that this IOMMU_HWPT_FAULT_ID_VALID is for nested domains?
> 
> So, aligning with that, here we need:
>         if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) || !user_data->len)
>                 return ERR_PTR(-EOPNOTSUPP);

I added a TEST_F for the coverage:

+TEST_F(iommufd_viommu, viommu_alloc_nested_iopf)
+{
+       struct iommu_hwpt_selftest data = {
+               .iotlb = IOMMU_TEST_IOTLB_DEFAULT,
+       };
+       uint32_t viommu_id = self->viommu_id;
+       uint32_t dev_id = self->device_id;
+       uint32_t iopf_hwpt_id;
+       uint32_t fault_id;
+       uint32_t fault_fd;
+
+       if (self->device_id) {
+               test_ioctl_fault_alloc(&fault_id, &fault_fd);
+               test_err_hwpt_alloc_iopf(
+                       ENOENT, dev_id, viommu_id, UINT32_MAX,
+                       IOMMU_HWPT_FAULT_ID_VALID, &iopf_hwpt_id,
+                       IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));
+               test_err_hwpt_alloc_iopf(
+                       EOPNOTSUPP, dev_id, viommu_id, fault_id,
+                       IOMMU_HWPT_FAULT_ID_VALID | (1 << 31), &iopf_hwpt_id,
+                       IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data));
+               test_cmd_hwpt_alloc_iopf(
+                       dev_id, viommu_id, fault_id, IOMMU_HWPT_FAULT_ID_VALID,
+                       &iopf_hwpt_id, IOMMU_HWPT_DATA_SELFTEST, &data,
+                       sizeof(data));
+
+               test_cmd_mock_domain_replace(self->stdev_id, iopf_hwpt_id);
+               EXPECT_ERRNO(EBUSY,
+                            _test_ioctl_destroy(self->fd, iopf_hwpt_id));
+               test_cmd_trigger_iopf(dev_id, fault_fd);
+
+               test_cmd_mock_domain_replace(self->stdev_id, self->ioas_id);
+               test_ioctl_destroy(iopf_hwpt_id);
+               close(fault_fd);
+               test_ioctl_destroy(fault_id);
+       }
+}

Thanks
Nicolin
Jason Gunthorpe Oct. 29, 2024, 3:27 p.m. UTC | #8
On Mon, Oct 28, 2024 at 07:52:10AM -0700, Nicolin Chen wrote:
> On Mon, Oct 28, 2024 at 10:03:09AM -0300, Jason Gunthorpe wrote:
> > On Mon, Oct 28, 2024 at 11:24:10AM +0800, Zhangfei Gao wrote:
> > 
> > > > +/**
> > > > + * iommufd_viommu_alloc_hwpt_nested() - Get a hwpt_nested for a vIOMMU
> > > > + * @viommu: vIOMMU ojbect to associate the hwpt_nested/domain with
> > > > + * @user_data: user_data pointer. Must be valid
> > > > + *
> > > > + * Allocate a new IOMMU_DOMAIN_NESTED for a vIOMMU and return it as a NESTED
> > > > + * hw_pagetable.
> > > > + */
> > > > +static struct iommufd_hwpt_nested *
> > > > +iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
> > > > +                                const struct iommu_user_data *user_data)
> > > > +{
> > > > +       struct iommufd_hwpt_nested *hwpt_nested;
> > > > +       struct iommufd_hw_pagetable *hwpt;
> > > > +       int rc;
> > > > +
> > > > +       if (flags)
> > > > +               return ERR_PTR(-EOPNOTSUPP);
> > > 
> > > This check should be removed.
> > > 
> > > When a user page fault is required, IOMMU_HWPT_FAULT_ID_VALID is set.
> > > if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {
> > 
> > It can't just be removed..
> > 
> > I suspect that IOMMU_HWPT_FAULT_ID_VALID should not be set on the
> > nested domain but on the parent?
> 
> By giving another look,
> 
> In iommufd_hwpt_paging_alloc(), we reject IOMMU_HWPT_FAULT_ID_VALID:
> 	const u32 valid_flags = IOMMU_HWPT_ALLOC_NEST_PARENT |
> 				IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
> 	...
> 	if (flags & ~valid_flags)
> 		return ERR_PTR(-EOPNOTSUPP);
> 
> In iommufd_hwpt_nested_alloc(), we mask the flag away:
> 	if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) ||
> 	    !user_data->len || !ops->domain_alloc_user)
> 		return ERR_PTR(-EOPNOTSUPP);
> 	...
> 	hwpt->domain = ops->domain_alloc_user(idev->dev,
> 					      flags & ~IOMMU_HWPT_FAULT_ID_VALID,
> 					      parent->common.domain, user_data);
> 
> Then, in the common function it has a section of
> 	if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {
> 	...
> 
> It seems that this IOMMU_HWPT_FAULT_ID_VALID is for nested domains?

OK, but ARM should be blocking it since it doesn't work there.

I think we made some error here, it should have been passed in flags
to the drivers and only intel should have accepted it.

This suggests we should send flags down the viommu alloc domain path too.

Jason
Nicolin Chen Oct. 29, 2024, 4:07 p.m. UTC | #9
On Tue, Oct 29, 2024 at 12:27:46PM -0300, Jason Gunthorpe wrote:
> On Mon, Oct 28, 2024 at 07:52:10AM -0700, Nicolin Chen wrote:
> > On Mon, Oct 28, 2024 at 10:03:09AM -0300, Jason Gunthorpe wrote:
> > In iommufd_hwpt_paging_alloc(), we reject IOMMU_HWPT_FAULT_ID_VALID:
> > 	const u32 valid_flags = IOMMU_HWPT_ALLOC_NEST_PARENT |
> > 				IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
> > 	...
> > 	if (flags & ~valid_flags)
> > 		return ERR_PTR(-EOPNOTSUPP);
> > 
> > In iommufd_hwpt_nested_alloc(), we mask the flag away:
> > 	if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) ||
> > 	    !user_data->len || !ops->domain_alloc_user)
> > 		return ERR_PTR(-EOPNOTSUPP);
> > 	...
> > 	hwpt->domain = ops->domain_alloc_user(idev->dev,
> > 					      flags & ~IOMMU_HWPT_FAULT_ID_VALID,
> > 					      parent->common.domain, user_data);
> > 
> > Then, in the common function it has a section of
> > 	if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {
> > 	...
> > 
> > It seems that this IOMMU_HWPT_FAULT_ID_VALID is for nested domains?
> 
> OK, but ARM should be blocking it since it doesn't work there.
> 
> I think we made some error here, it should have been passed in flags
> to the drivers and only intel should have accepted it.

Trying to limit changes here since two parts are already quite
large, I think a separate series fixing that would be nicer? 

> This suggests we should send flags down the viommu alloc domain path too.

Ack. Will pass it in.

Thanks
Nicolin
Jason Gunthorpe Oct. 29, 2024, 6:53 p.m. UTC | #10
On Tue, Oct 29, 2024 at 09:07:38AM -0700, Nicolin Chen wrote:
> On Tue, Oct 29, 2024 at 12:27:46PM -0300, Jason Gunthorpe wrote:
> > On Mon, Oct 28, 2024 at 07:52:10AM -0700, Nicolin Chen wrote:
> > > On Mon, Oct 28, 2024 at 10:03:09AM -0300, Jason Gunthorpe wrote:
> > > In iommufd_hwpt_paging_alloc(), we reject IOMMU_HWPT_FAULT_ID_VALID:
> > > 	const u32 valid_flags = IOMMU_HWPT_ALLOC_NEST_PARENT |
> > > 				IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
> > > 	...
> > > 	if (flags & ~valid_flags)
> > > 		return ERR_PTR(-EOPNOTSUPP);
> > > 
> > > In iommufd_hwpt_nested_alloc(), we mask the flag away:
> > > 	if ((flags & ~IOMMU_HWPT_FAULT_ID_VALID) ||
> > > 	    !user_data->len || !ops->domain_alloc_user)
> > > 		return ERR_PTR(-EOPNOTSUPP);
> > > 	...
> > > 	hwpt->domain = ops->domain_alloc_user(idev->dev,
> > > 					      flags & ~IOMMU_HWPT_FAULT_ID_VALID,
> > > 					      parent->common.domain, user_data);
> > > 
> > > Then, in the common function it has a section of
> > > 	if (cmd->flags & IOMMU_HWPT_FAULT_ID_VALID) {
> > > 	...
> > > 
> > > It seems that this IOMMU_HWPT_FAULT_ID_VALID is for nested domains?
> > 
> > OK, but ARM should be blocking it since it doesn't work there.
> > 
> > I think we made some error here, it should have been passed in flags
> > to the drivers and only intel should have accepted it.
> 
> Trying to limit changes here since two parts are already quite
> large, I think a separate series fixing that would be nicer? 

Yes, let's just make a note

> > This suggests we should send flags down the viommu alloc domain path too.
> 
> Ack. Will pass it in.

But this would be nice to get to

Jason
diff mbox series

Patch

diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 9adf8d616796..8c9ab35eaea5 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -288,6 +288,7 @@  struct iommufd_hwpt_paging {
 struct iommufd_hwpt_nested {
 	struct iommufd_hw_pagetable common;
 	struct iommufd_hwpt_paging *parent;
+	struct iommufd_viommu *viommu;
 };
 
 static inline bool hwpt_is_paging(struct iommufd_hw_pagetable *hwpt)
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index 3d320d069654..717659b9fdce 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -430,7 +430,7 @@  enum iommu_hwpt_data_type {
  * @size: sizeof(struct iommu_hwpt_alloc)
  * @flags: Combination of enum iommufd_hwpt_alloc_flags
  * @dev_id: The device to allocate this HWPT for
- * @pt_id: The IOAS or HWPT to connect this HWPT to
+ * @pt_id: The IOAS or HWPT or vIOMMU to connect this HWPT to
  * @out_hwpt_id: The ID of the new HWPT
  * @__reserved: Must be 0
  * @data_type: One of enum iommu_hwpt_data_type
@@ -449,11 +449,13 @@  enum iommu_hwpt_data_type {
  * IOMMU_HWPT_DATA_NONE. The HWPT can be allocated as a parent HWPT for a
  * nesting configuration by passing IOMMU_HWPT_ALLOC_NEST_PARENT via @flags.
  *
- * A user-managed nested HWPT will be created from a given parent HWPT via
- * @pt_id, in which the parent HWPT must be allocated previously via the
- * same ioctl from a given IOAS (@pt_id). In this case, the @data_type
- * must be set to a pre-defined type corresponding to an I/O page table
- * type supported by the underlying IOMMU hardware.
+ * A user-managed nested HWPT will be created from a given vIOMMU (wrapping a
+ * parent HWPT) or a parent HWPT via @pt_id, in which the parent HWPT must be
+ * allocated previously via the same ioctl from a given IOAS (@pt_id). In this
+ * case, the @data_type must be set to a pre-defined type corresponding to an
+ * I/O page table type supported by the underlying IOMMU hardware. The device
+ * via @dev_id and the vIOMMU via @pt_id must be associated to the same IOMMU
+ * instance.
  *
  * If the @data_type is set to IOMMU_HWPT_DATA_NONE, @data_len and
  * @data_uptr should be zero. Otherwise, both @data_len and @data_uptr
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index d06bf6e6c19f..1df5d40c93df 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -57,7 +57,10 @@  void iommufd_hwpt_nested_destroy(struct iommufd_object *obj)
 		container_of(obj, struct iommufd_hwpt_nested, common.obj);
 
 	__iommufd_hwpt_destroy(&hwpt_nested->common);
-	refcount_dec(&hwpt_nested->parent->common.obj.users);
+	if (hwpt_nested->viommu)
+		refcount_dec(&hwpt_nested->viommu->obj.users);
+	else
+		refcount_dec(&hwpt_nested->parent->common.obj.users);
 }
 
 void iommufd_hwpt_nested_abort(struct iommufd_object *obj)
@@ -260,6 +263,56 @@  iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx,
 	return ERR_PTR(rc);
 }
 
+/**
+ * iommufd_viommu_alloc_hwpt_nested() - Get a hwpt_nested for a vIOMMU
+ * @viommu: vIOMMU ojbect to associate the hwpt_nested/domain with
+ * @user_data: user_data pointer. Must be valid
+ *
+ * Allocate a new IOMMU_DOMAIN_NESTED for a vIOMMU and return it as a NESTED
+ * hw_pagetable.
+ */
+static struct iommufd_hwpt_nested *
+iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
+				 const struct iommu_user_data *user_data)
+{
+	struct iommufd_hwpt_nested *hwpt_nested;
+	struct iommufd_hw_pagetable *hwpt;
+	int rc;
+
+	if (flags)
+		return ERR_PTR(-EOPNOTSUPP);
+	if (!viommu->ops || !viommu->ops->alloc_domain_nested)
+		return ERR_PTR(-EOPNOTSUPP);
+
+	hwpt_nested = __iommufd_object_alloc(
+		viommu->ictx, hwpt_nested, IOMMUFD_OBJ_HWPT_NESTED, common.obj);
+	if (IS_ERR(hwpt_nested))
+		return ERR_CAST(hwpt_nested);
+	hwpt = &hwpt_nested->common;
+
+	hwpt_nested->viommu = viommu;
+	hwpt_nested->parent = viommu->hwpt;
+	refcount_inc(&viommu->obj.users);
+
+	hwpt->domain = viommu->ops->alloc_domain_nested(viommu, user_data);
+	if (IS_ERR(hwpt->domain)) {
+		rc = PTR_ERR(hwpt->domain);
+		hwpt->domain = NULL;
+		goto out_abort;
+	}
+	hwpt->domain->owner = viommu->iommu_dev->ops;
+
+	if (WARN_ON_ONCE(hwpt->domain->type != IOMMU_DOMAIN_NESTED)) {
+		rc = -EINVAL;
+		goto out_abort;
+	}
+	return hwpt_nested;
+
+out_abort:
+	iommufd_object_abort_and_destroy(viommu->ictx, &hwpt->obj);
+	return ERR_PTR(rc);
+}
+
 int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
 {
 	struct iommu_hwpt_alloc *cmd = ucmd->cmd;
@@ -316,6 +369,22 @@  int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
 			goto out_unlock;
 		}
 		hwpt = &hwpt_nested->common;
+	} else if (pt_obj->type == IOMMUFD_OBJ_VIOMMU) {
+		struct iommufd_hwpt_nested *hwpt_nested;
+		struct iommufd_viommu *viommu;
+
+		viommu = container_of(pt_obj, struct iommufd_viommu, obj);
+		if (viommu->iommu_dev != __iommu_get_iommu_dev(idev->dev)) {
+			rc = -EINVAL;
+			goto out_unlock;
+		}
+		hwpt_nested = iommufd_viommu_alloc_hwpt_nested(
+			viommu, cmd->flags, &user_data);
+		if (IS_ERR(hwpt_nested)) {
+			rc = PTR_ERR(hwpt_nested);
+			goto out_unlock;
+		}
+		hwpt = &hwpt_nested->common;
 	} else {
 		rc = -EINVAL;
 		goto out_put_pt;