diff mbox series

[v1,1/5] iommufd: Create access in vfio_iommufd_emulated_bind()

Message ID 20230308131340.459224-2-yi.l.liu@intel.com (mailing list archive)
State New, archived
Headers show
Series vfio: Make emulated devices prepared for vfio device cdev | expand

Commit Message

Yi Liu March 8, 2023, 1:13 p.m. UTC
From: Nicolin Chen <nicolinc@nvidia.com>

There are needs to created iommufd_access prior to have an IOAS and set
IOAS later. Like the vfio device cdev needs to have an iommufd object
to represent the bond (iommufd_access) and IOAS replacement.

This moves iommufd_access_create() call into vfio_iommufd_emulated_bind(),
making it symmetric with the __vfio_iommufd_access_destroy() call in
vfio_iommufd_emulated_unbind(). This means an access is created/destroyed
by the bind()/unbind(), and the vfio_iommufd_emulated_attach_ioas() only
updates the access->ioas pointer.

Since there's no longer an ioas_id input for iommufd_access_create(), add
a new helper iommufd_access_set_ioas() to set access->ioas. We can later
add a "replace" feature simply to the new iommufd_access_set_ioas() too.

Leaving the access->ioas in vfio_iommufd_emulated_attach_ioas(), however,
can introduce some potential of a race condition during pin_/unpin_pages()
call where access->ioas->iopt is getting referenced. So, add an ioas_lock
to protect it.

Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
---
 drivers/iommu/iommufd/device.c          | 103 ++++++++++++++++++------
 drivers/iommu/iommufd/iommufd_private.h |   1 +
 drivers/iommu/iommufd/selftest.c        |   5 +-
 drivers/vfio/iommufd.c                  |  23 +++---
 include/linux/iommufd.h                 |   3 +-
 5 files changed, 100 insertions(+), 35 deletions(-)

Comments

Tian, Kevin March 10, 2023, 2:08 a.m. UTC | #1
> From: Liu, Yi L <yi.l.liu@intel.com>
> Sent: Wednesday, March 8, 2023 9:14 PM
>
> @@ -449,33 +450,18 @@ iommufd_access_create(struct iommufd_ctx *ictx,
> u32 ioas_id,
>  	access->data = data;
>  	access->ops = ops;
> 
> -	obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> -	if (IS_ERR(obj)) {
> -		rc = PTR_ERR(obj);
> -		goto out_abort;
> -	}
> -	access->ioas = container_of(obj, struct iommufd_ioas, obj);
> -	iommufd_ref_to_users(obj);
> -
>  	if (ops->needs_pin_pages)
>  		access->iova_alignment = PAGE_SIZE;
>  	else
>  		access->iova_alignment = 1;
> -	rc = iopt_add_access(&access->ioas->iopt, access);
> -	if (rc)
> -		goto out_put_ioas;
> 
>  	/* The calling driver is a user until iommufd_access_destroy() */
>  	refcount_inc(&access->obj.users);
> +	mutex_init(&access->ioas_lock);
>  	access->ictx = ictx;
>  	iommufd_ctx_get(ictx);

this refcnt get should be moved to the start given next patch
removes the reference in the caller side.
Jason Gunthorpe March 10, 2023, 5:36 p.m. UTC | #2
On Wed, Mar 08, 2023 at 05:13:36AM -0800, Yi Liu wrote:

> +int iommufd_access_set_ioas(struct iommufd_access *access, u32 ioas_id)
> +{
> +	struct iommufd_ioas *new_ioas = NULL, *cur_ioas;
> +	struct iommufd_ctx *ictx = access->ictx;
> +	struct iommufd_object *obj;
> +	int rc = 0;
> +
> +	if (ioas_id) {
> +		obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> +		if (IS_ERR(obj))
> +			return PTR_ERR(obj);
> +		new_ioas = container_of(obj, struct iommufd_ioas, obj);
> +	}
> +
> +	mutex_lock(&access->ioas_lock);
> +	cur_ioas = access->ioas;
> +	if (cur_ioas == new_ioas)
> +		goto out_unlock;
> +
> +	if (new_ioas) {
> +		rc = iopt_add_access(&new_ioas->iopt, access);
> +		if (rc)
> +			goto out_unlock;
> +		iommufd_ref_to_users(obj);
> +	}
> +
> +	if (cur_ioas) {
> +		iopt_remove_access(&cur_ioas->iopt, access);
> +		refcount_dec(&cur_ioas->obj.users);
> +	}

This should match the physical side with an add/remove/replace
API. Especially since remove is implicit in destroy this series only
needs the add API

And the locking shouldn't come in another patch that brings the
replace/remove since with just split add we don't need it.

That will make this patch alot smaller

Jason
Nicolin Chen March 14, 2023, 8:20 a.m. UTC | #3
On Fri, Mar 10, 2023 at 01:36:22PM -0400, Jason Gunthorpe wrote:
> On Wed, Mar 08, 2023 at 05:13:36AM -0800, Yi Liu wrote:
> 
> > +int iommufd_access_set_ioas(struct iommufd_access *access, u32 ioas_id)
> > +{
> > +	struct iommufd_ioas *new_ioas = NULL, *cur_ioas;
> > +	struct iommufd_ctx *ictx = access->ictx;
> > +	struct iommufd_object *obj;
> > +	int rc = 0;
> > +
> > +	if (ioas_id) {
> > +		obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> > +		if (IS_ERR(obj))
> > +			return PTR_ERR(obj);
> > +		new_ioas = container_of(obj, struct iommufd_ioas, obj);
> > +	}
> > +
> > +	mutex_lock(&access->ioas_lock);
> > +	cur_ioas = access->ioas;
> > +	if (cur_ioas == new_ioas)
> > +		goto out_unlock;
> > +
> > +	if (new_ioas) {
> > +		rc = iopt_add_access(&new_ioas->iopt, access);
> > +		if (rc)
> > +			goto out_unlock;
> > +		iommufd_ref_to_users(obj);
> > +	}
> > +
> > +	if (cur_ioas) {
> > +		iopt_remove_access(&cur_ioas->iopt, access);
> > +		refcount_dec(&cur_ioas->obj.users);
> > +	}
> 
> This should match the physical side with an add/remove/replace
> API. Especially since remove is implicit in destroy this series only
> needs the add API

I assume that the API would be iommufd_access_attach,
iommufd_access_detach, and iommufd_access_replace(). And there
might be an iommufd_access_change_pt(access, pt, bool replace)?

> And the locking shouldn't come in another patch that brings the
> replace/remove since with just split add we don't need it.

Hmm. The iommufd_access_detach would be needed in the following
cdev series, while the iommufd_access_replace would be need in
my replace series. So, that would make the API be divided into
three series.

Perhaps we can have iommufd_access_attach/detach in this series
along with a vfio_iommufd_emulated_detach_ioas, and the locking
will come with another patch in replace series?

Thanks
Nic
Nicolin Chen March 14, 2023, 6:50 p.m. UTC | #4
On Fri, Mar 10, 2023 at 02:08:15AM +0000, Tian, Kevin wrote:
> External email: Use caution opening links or attachments
> 
> 
> > From: Liu, Yi L <yi.l.liu@intel.com>
> > Sent: Wednesday, March 8, 2023 9:14 PM
> >
> > @@ -449,33 +450,18 @@ iommufd_access_create(struct iommufd_ctx *ictx,
> > u32 ioas_id,
> >       access->data = data;
> >       access->ops = ops;
> >
> > -     obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> > -     if (IS_ERR(obj)) {
> > -             rc = PTR_ERR(obj);
> > -             goto out_abort;
> > -     }
> > -     access->ioas = container_of(obj, struct iommufd_ioas, obj);
> > -     iommufd_ref_to_users(obj);
> > -
> >       if (ops->needs_pin_pages)
> >               access->iova_alignment = PAGE_SIZE;
> >       else
> >               access->iova_alignment = 1;
> > -     rc = iopt_add_access(&access->ioas->iopt, access);
> > -     if (rc)
> > -             goto out_put_ioas;
> >
> >       /* The calling driver is a user until iommufd_access_destroy() */
> >       refcount_inc(&access->obj.users);
> > +     mutex_init(&access->ioas_lock);
> >       access->ictx = ictx;
> >       iommufd_ctx_get(ictx);
> 
> this refcnt get should be moved to the start given next patch
> removes the reference in the caller side.

I don't feel quite convincing to justify for moving it in this
patch. Perhaps we should do that in the following patch, where
it needs this? Or another individual patch moving this alone?

Thanks
Nic
Nicolin Chen March 15, 2023, 1:01 a.m. UTC | #5
Hi Jason/Kevin,

On Tue, Mar 14, 2023 at 01:20:52AM -0700, Nicolin Chen wrote:
> On Fri, Mar 10, 2023 at 01:36:22PM -0400, Jason Gunthorpe wrote:
> > On Wed, Mar 08, 2023 at 05:13:36AM -0800, Yi Liu wrote:
> > 
> > > +int iommufd_access_set_ioas(struct iommufd_access *access, u32 ioas_id)
> > > +{
> > > +	struct iommufd_ioas *new_ioas = NULL, *cur_ioas;
> > > +	struct iommufd_ctx *ictx = access->ictx;
> > > +	struct iommufd_object *obj;
> > > +	int rc = 0;
> > > +
> > > +	if (ioas_id) {
> > > +		obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> > > +		if (IS_ERR(obj))
> > > +			return PTR_ERR(obj);
> > > +		new_ioas = container_of(obj, struct iommufd_ioas, obj);
> > > +	}
> > > +
> > > +	mutex_lock(&access->ioas_lock);
> > > +	cur_ioas = access->ioas;
> > > +	if (cur_ioas == new_ioas)
> > > +		goto out_unlock;
> > > +
> > > +	if (new_ioas) {
> > > +		rc = iopt_add_access(&new_ioas->iopt, access);
> > > +		if (rc)
> > > +			goto out_unlock;
> > > +		iommufd_ref_to_users(obj);
> > > +	}
> > > +
> > > +	if (cur_ioas) {
> > > +		iopt_remove_access(&cur_ioas->iopt, access);
> > > +		refcount_dec(&cur_ioas->obj.users);
> > > +	}
> > 
> > This should match the physical side with an add/remove/replace
> > API. Especially since remove is implicit in destroy this series only
> > needs the add API
> 
> I assume that the API would be iommufd_access_attach,
> iommufd_access_detach, and iommufd_access_replace(). And there
> might be an iommufd_access_change_pt(access, pt, bool replace)?
> 
> > And the locking shouldn't come in another patch that brings the
> > replace/remove since with just split add we don't need it.
> 
> Hmm. The iommufd_access_detach would be needed in the following
> cdev series, while the iommufd_access_replace would be need in
> my replace series. So, that would make the API be divided into
> three series.
> 
> Perhaps we can have iommufd_access_attach/detach in this series
> along with a vfio_iommufd_emulated_detach_ioas, and the locking
> will come with another patch in replace series?

I recall that we previously concluded that the unbind() is safe
to go without doing access->ops->unmap, because close_device()
would be called prior to the unbind().

But, to add the vfio_iommufd_emulated_detach_ioas() in the cdev
series, we'd need the access->ops->unmap call, and the locking
and "ioas_unpin" too in the detach and attach APIs, right?

I tried a bit of some update, across this series, cdev series,
and the replace series. Though we might be able to simplify a
bit of this patch/series, yet it doesn't seem to simplify the
changes overall, particularly in the following cdev series for
having an unmap() call and the locking.

Then the replace API would mostly overlap with the attach API,
by only having an additional detach(), which means actually we
only need an iommufd_access_attach API to cover both cases...

Can you please take a look at the final access APIs that I've
attached at the end of the email for things mentioned above?
Hopefully we can confirm and put them correctly into the next
version of the three series.

Thanks
Nic

-----------------------------------------------------------------------
static void __iommufd_access_detach(struct iommufd_access *access)
{
	struct iommufd_ioas *cur_ioas = access->ioas;

	lockdep_assert_held(&access->ioas_lock);
	/*
	 * Set ioas to NULL to block any further iommufd_access_pin_pages().
	 * iommufd_access_unpin_pages() can continue using access->ioas_unpin.
	 */
	access->ioas = NULL;

	if (access->ops->unmap) {
		mutex_unlock(&access->ioas_lock);
		access->ops->unmap(access->data, 0, ULONG_MAX);
		mutex_lock(&access->ioas_lock);
	}
	iopt_remove_access(&cur_ioas->iopt, access);
	refcount_dec(&cur_ioas->obj.users);
}

static int iommufd_access_change_pt(struct iommufd_access *access, u32 ioas_id)
{
	struct iommufd_ioas *new_ioas, *cur_ioas;
	struct iommufd_object *obj;
	int rc = 0;

	obj = iommufd_get_object(access->ictx, ioas_id, IOMMUFD_OBJ_IOAS);
	if (IS_ERR(obj))
		return PTR_ERR(obj);
	new_ioas = container_of(obj, struct iommufd_ioas, obj);

	mutex_lock(&access->ioas_lock);
	cur_ioas = access->ioas;
	if (cur_ioas == new_ioas)
		goto out_unlock;

	rc = iopt_add_access(&new_ioas->iopt, access);
	if (rc)
		goto out_unlock;
	iommufd_ref_to_users(obj);

	if (cur_ioas)
		__iommufd_access_detach(access);
	access->ioas_unpin = new_ioas;
	access->ioas = new_ioas;
	mutex_unlock(&access->ioas_lock);
	return 0;

out_unlock:
	mutex_unlock(&access->ioas_lock);
	iommufd_put_object(obj);
	return rc;
}

int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id)
{
	return iommufd_access_change_pt(access, ioas_id);
}
EXPORT_SYMBOL_NS_GPL(iommufd_access_attach, IOMMUFD);

/* Identical to iommufd_access_attach now... */
int iommufd_access_replace(struct iommufd_access *access, u32 ioas_id)
{
	return iommufd_access_change_pt(access, ioas_id);
}
EXPORT_SYMBOL_NS_GPL(iommufd_access_replace, IOMMUFD);

void iommufd_access_detach(struct iommufd_access *access)
{
	mutex_lock(&access->ioas_lock);
	if (WARN_ON(!access->ioas))
		goto out;
	__iommufd_access_detach(access);
out:
	access->ioas_unpin = NULL;
	mutex_unlock(&access->ioas_lock);
}
EXPORT_SYMBOL_NS_GPL(iommufd_access_detach, IOMMUFD);
Tian, Kevin March 15, 2023, 6:15 a.m. UTC | #6
> From: Nicolin Chen <nicolinc@nvidia.com>
> Sent: Wednesday, March 15, 2023 9:01 AM
> 
> Hi Jason/Kevin,
> 
> >
> > Perhaps we can have iommufd_access_attach/detach in this series
> > along with a vfio_iommufd_emulated_detach_ioas, and the locking
> > will come with another patch in replace series?
> 
> I recall that we previously concluded that the unbind() is safe
> to go without doing access->ops->unmap, because close_device()
> would be called prior to the unbind().
> 
> But, to add the vfio_iommufd_emulated_detach_ioas() in the cdev
> series, we'd need the access->ops->unmap call, and the locking
> and "ioas_unpin" too in the detach and attach APIs, right?

yes. We need locking since detach can happen any time with 
cdev while driver is conducting pinning.

> 
> I tried a bit of some update, across this series, cdev series,
> and the replace series. Though we might be able to simplify a
> bit of this patch/series, yet it doesn't seem to simplify the
> changes overall, particularly in the following cdev series for
> having an unmap() call and the locking.
> 
> Then the replace API would mostly overlap with the attach API,
> by only having an additional detach(), which means actually we
> only need an iommufd_access_attach API to cover both cases...

there is a subtle difference. to match the physical path:

for attach we expect the existing access->ioas is either NULL or
same as the new ioas.

for replace access->ioas must exist.

they need different condition checks.
Tian, Kevin March 15, 2023, 6:16 a.m. UTC | #7
> From: Nicolin Chen
> Sent: Wednesday, March 15, 2023 2:51 AM
> 
> On Fri, Mar 10, 2023 at 02:08:15AM +0000, Tian, Kevin wrote:
> > External email: Use caution opening links or attachments
> >
> >
> > > From: Liu, Yi L <yi.l.liu@intel.com>
> > > Sent: Wednesday, March 8, 2023 9:14 PM
> > >
> > > @@ -449,33 +450,18 @@ iommufd_access_create(struct iommufd_ctx
> *ictx,
> > > u32 ioas_id,
> > >       access->data = data;
> > >       access->ops = ops;
> > >
> > > -     obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> > > -     if (IS_ERR(obj)) {
> > > -             rc = PTR_ERR(obj);
> > > -             goto out_abort;
> > > -     }
> > > -     access->ioas = container_of(obj, struct iommufd_ioas, obj);
> > > -     iommufd_ref_to_users(obj);
> > > -
> > >       if (ops->needs_pin_pages)
> > >               access->iova_alignment = PAGE_SIZE;
> > >       else
> > >               access->iova_alignment = 1;
> > > -     rc = iopt_add_access(&access->ioas->iopt, access);
> > > -     if (rc)
> > > -             goto out_put_ioas;
> > >
> > >       /* The calling driver is a user until iommufd_access_destroy() */
> > >       refcount_inc(&access->obj.users);
> > > +     mutex_init(&access->ioas_lock);
> > >       access->ictx = ictx;
> > >       iommufd_ctx_get(ictx);
> >
> > this refcnt get should be moved to the start given next patch
> > removes the reference in the caller side.
> 
> I don't feel quite convincing to justify for moving it in this
> patch. Perhaps we should do that in the following patch, where
> it needs this? Or another individual patch moving this alone?
> 

Next patch. I just tried to point out the required change caused
by next patch. 
Nicolin Chen March 15, 2023, 6:21 a.m. UTC | #8
On Wed, Mar 15, 2023 at 06:16:37AM +0000, Tian, Kevin wrote:
> External email: Use caution opening links or attachments
> 
> 
> > From: Nicolin Chen
> > Sent: Wednesday, March 15, 2023 2:51 AM
> >
> > On Fri, Mar 10, 2023 at 02:08:15AM +0000, Tian, Kevin wrote:
> > > External email: Use caution opening links or attachments
> > >
> > >
> > > > From: Liu, Yi L <yi.l.liu@intel.com>
> > > > Sent: Wednesday, March 8, 2023 9:14 PM
> > > >
> > > > @@ -449,33 +450,18 @@ iommufd_access_create(struct iommufd_ctx
> > *ictx,
> > > > u32 ioas_id,
> > > >       access->data = data;
> > > >       access->ops = ops;
> > > >
> > > > -     obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> > > > -     if (IS_ERR(obj)) {
> > > > -             rc = PTR_ERR(obj);
> > > > -             goto out_abort;
> > > > -     }
> > > > -     access->ioas = container_of(obj, struct iommufd_ioas, obj);
> > > > -     iommufd_ref_to_users(obj);
> > > > -
> > > >       if (ops->needs_pin_pages)
> > > >               access->iova_alignment = PAGE_SIZE;
> > > >       else
> > > >               access->iova_alignment = 1;
> > > > -     rc = iopt_add_access(&access->ioas->iopt, access);
> > > > -     if (rc)
> > > > -             goto out_put_ioas;
> > > >
> > > >       /* The calling driver is a user until iommufd_access_destroy() */
> > > >       refcount_inc(&access->obj.users);
> > > > +     mutex_init(&access->ioas_lock);
> > > >       access->ictx = ictx;
> > > >       iommufd_ctx_get(ictx);
> > >
> > > this refcnt get should be moved to the start given next patch
> > > removes the reference in the caller side.
> >
> > I don't feel quite convincing to justify for moving it in this
> > patch. Perhaps we should do that in the following patch, where
> > it needs this? Or another individual patch moving this alone?
> >
> 
> Next patch. I just tried to point out the required change caused
> by next patch. 
Nicolin Chen March 15, 2023, 6:32 a.m. UTC | #9
On Wed, Mar 15, 2023 at 06:15:23AM +0000, Tian, Kevin wrote:
> External email: Use caution opening links or attachments
> 
> 
> > From: Nicolin Chen <nicolinc@nvidia.com>
> > Sent: Wednesday, March 15, 2023 9:01 AM
> >
> > Hi Jason/Kevin,
> >
> > >
> > > Perhaps we can have iommufd_access_attach/detach in this series
> > > along with a vfio_iommufd_emulated_detach_ioas, and the locking
> > > will come with another patch in replace series?
> >
> > I recall that we previously concluded that the unbind() is safe
> > to go without doing access->ops->unmap, because close_device()
> > would be called prior to the unbind().
> >
> > But, to add the vfio_iommufd_emulated_detach_ioas() in the cdev
> > series, we'd need the access->ops->unmap call, and the locking
> > and "ioas_unpin" too in the detach and attach APIs, right?
> 
> yes. We need locking since detach can happen any time with
> cdev while driver is conducting pinning.

So, this preparatory series will add a pair of simple attach()
and detach() APIs. Then the cdev series will add the locking
and the ioas_unpin stuff as a rework of the detach() API.

> > I tried a bit of some update, across this series, cdev series,
> > and the replace series. Though we might be able to simplify a
> > bit of this patch/series, yet it doesn't seem to simplify the
> > changes overall, particularly in the following cdev series for
> > having an unmap() call and the locking.
> >
> > Then the replace API would mostly overlap with the attach API,
> > by only having an additional detach(), which means actually we
> > only need an iommufd_access_attach API to cover both cases...
> 
> there is a subtle difference. to match the physical path:
> 
> for attach we expect the existing access->ioas is either NULL or
> same as the new ioas.
> 
> for replace access->ioas must exist.
> 
> they need different condition checks.

I think they can be something mingled... the sample code that
I sent previously could take care of those conditions. But, I
am also thinking a bit that maybe attach() does not need the
locking? I can do a separate replace() function in this case.

Thanks
Nic
Tian, Kevin March 15, 2023, 6:50 a.m. UTC | #10
> From: Nicolin Chen
> Sent: Wednesday, March 15, 2023 2:33 PM
> 
> On Wed, Mar 15, 2023 at 06:15:23AM +0000, Tian, Kevin wrote:
> > External email: Use caution opening links or attachments
> >
> >
> > > From: Nicolin Chen <nicolinc@nvidia.com>
> > > Sent: Wednesday, March 15, 2023 9:01 AM
> > >
> > > Hi Jason/Kevin,
> > >
> > > >
> > > > Perhaps we can have iommufd_access_attach/detach in this series
> > > > along with a vfio_iommufd_emulated_detach_ioas, and the locking
> > > > will come with another patch in replace series?
> > >
> > > I recall that we previously concluded that the unbind() is safe
> > > to go without doing access->ops->unmap, because close_device()
> > > would be called prior to the unbind().
> > >
> > > But, to add the vfio_iommufd_emulated_detach_ioas() in the cdev
> > > series, we'd need the access->ops->unmap call, and the locking
> > > and "ioas_unpin" too in the detach and attach APIs, right?
> >
> > yes. We need locking since detach can happen any time with
> > cdev while driver is conducting pinning.
> 
> So, this preparatory series will add a pair of simple attach()
> and detach() APIs. Then the cdev series will add the locking
> and the ioas_unpin stuff as a rework of the detach() API.
> 
> > > I tried a bit of some update, across this series, cdev series,
> > > and the replace series. Though we might be able to simplify a
> > > bit of this patch/series, yet it doesn't seem to simplify the
> > > changes overall, particularly in the following cdev series for
> > > having an unmap() call and the locking.
> > >
> > > Then the replace API would mostly overlap with the attach API,
> > > by only having an additional detach(), which means actually we
> > > only need an iommufd_access_attach API to cover both cases...
> >
> > there is a subtle difference. to match the physical path:
> >
> > for attach we expect the existing access->ioas is either NULL or
> > same as the new ioas.
> >
> > for replace access->ioas must exist.
> >
> > they need different condition checks.
> 
> I think they can be something mingled... the sample code that
> I sent previously could take care of those conditions. But, I
> am also thinking a bit that maybe attach() does not need the
> locking? I can do a separate replace() function in this case.
> 

w/o locking then you need smp_store_release() and its pair.

anyway it's not in perf critical path. Keeping lock for attach
is simpler and safe.
Tian, Kevin March 15, 2023, 6:52 a.m. UTC | #11
> From: Nicolin Chen
> Sent: Wednesday, March 15, 2023 2:22 PM
> 
> On Wed, Mar 15, 2023 at 06:16:37AM +0000, Tian, Kevin wrote:
> > External email: Use caution opening links or attachments
> >
> >
> > > From: Nicolin Chen
> > > Sent: Wednesday, March 15, 2023 2:51 AM
> > >
> > > On Fri, Mar 10, 2023 at 02:08:15AM +0000, Tian, Kevin wrote:
> > > > External email: Use caution opening links or attachments
> > > >
> > > >
> > > > > From: Liu, Yi L <yi.l.liu@intel.com>
> > > > > Sent: Wednesday, March 8, 2023 9:14 PM
> > > > >
> > > > > @@ -449,33 +450,18 @@ iommufd_access_create(struct
> iommufd_ctx
> > > *ictx,
> > > > > u32 ioas_id,
> > > > >       access->data = data;
> > > > >       access->ops = ops;
> > > > >
> > > > > -     obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> > > > > -     if (IS_ERR(obj)) {
> > > > > -             rc = PTR_ERR(obj);
> > > > > -             goto out_abort;
> > > > > -     }
> > > > > -     access->ioas = container_of(obj, struct iommufd_ioas, obj);
> > > > > -     iommufd_ref_to_users(obj);
> > > > > -
> > > > >       if (ops->needs_pin_pages)
> > > > >               access->iova_alignment = PAGE_SIZE;
> > > > >       else
> > > > >               access->iova_alignment = 1;
> > > > > -     rc = iopt_add_access(&access->ioas->iopt, access);
> > > > > -     if (rc)
> > > > > -             goto out_put_ioas;
> > > > >
> > > > >       /* The calling driver is a user until iommufd_access_destroy() */
> > > > >       refcount_inc(&access->obj.users);
> > > > > +     mutex_init(&access->ioas_lock);
> > > > >       access->ictx = ictx;
> > > > >       iommufd_ctx_get(ictx);
> > > >
> > > > this refcnt get should be moved to the start given next patch
> > > > removes the reference in the caller side.
> > >
> > > I don't feel quite convincing to justify for moving it in this
> > > patch. Perhaps we should do that in the following patch, where
> > > it needs this? Or another individual patch moving this alone?
> > >
> >
> > Next patch. I just tried to point out the required change caused
> > by next patch. 
Yi Liu March 15, 2023, 8:52 a.m. UTC | #12
> From: Tian, Kevin <kevin.tian@intel.com>
> Sent: Wednesday, March 15, 2023 2:52 PM
> 
> > From: Nicolin Chen
> > Sent: Wednesday, March 15, 2023 2:22 PM
> >
> > On Wed, Mar 15, 2023 at 06:16:37AM +0000, Tian, Kevin wrote:
> > > External email: Use caution opening links or attachments
> > >
> > >
> > > > From: Nicolin Chen
> > > > Sent: Wednesday, March 15, 2023 2:51 AM
> > > >
> > > > On Fri, Mar 10, 2023 at 02:08:15AM +0000, Tian, Kevin wrote:
> > > > > External email: Use caution opening links or attachments
> > > > >
> > > > >
> > > > > > From: Liu, Yi L <yi.l.liu@intel.com>
> > > > > > Sent: Wednesday, March 8, 2023 9:14 PM
> > > > > >
> > > > > > @@ -449,33 +450,18 @@ iommufd_access_create(struct
> > iommufd_ctx
> > > > *ictx,
> > > > > > u32 ioas_id,
> > > > > >       access->data = data;
> > > > > >       access->ops = ops;
> > > > > >
> > > > > > -     obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> > > > > > -     if (IS_ERR(obj)) {
> > > > > > -             rc = PTR_ERR(obj);
> > > > > > -             goto out_abort;
> > > > > > -     }
> > > > > > -     access->ioas = container_of(obj, struct iommufd_ioas, obj);
> > > > > > -     iommufd_ref_to_users(obj);
> > > > > > -
> > > > > >       if (ops->needs_pin_pages)
> > > > > >               access->iova_alignment = PAGE_SIZE;
> > > > > >       else
> > > > > >               access->iova_alignment = 1;
> > > > > > -     rc = iopt_add_access(&access->ioas->iopt, access);
> > > > > > -     if (rc)
> > > > > > -             goto out_put_ioas;
> > > > > >
> > > > > >       /* The calling driver is a user until iommufd_access_destroy() */
> > > > > >       refcount_inc(&access->obj.users);
> > > > > > +     mutex_init(&access->ioas_lock);
> > > > > >       access->ictx = ictx;
> > > > > >       iommufd_ctx_get(ictx);
> > > > >
> > > > > this refcnt get should be moved to the start given next patch
> > > > > removes the reference in the caller side.

This change is ok but seems not necessary.

Yes, vfio_iommufd_emulated_bind() will not have reference on the
ictx after the next patch. However, it gets reference only because it
wants to store it in vfio_device. Now, it does not store it. So no get.
I think the caller of vfio_iommufd_emulated_bind() should ensure
the ictx is valid. Also check the physical device bind. So maybe not
necessary to get ictx before calling iommufd_access_create(). This is
the same with the vfio_iommufd_physical_bind() which calls
iommufd_device_bind() without ictx get, and iommufd_device_bind()
also gets ictx in the end.
 
If it's really necessary, maybe let the vfio_iommufd_physical_bind()
and vfio_iommufd_emulated_bind() get/put ictx around calling
iommufd_access_create()/iommufd_device_bind().

> > > >
> > > > I don't feel quite convincing to justify for moving it in this
> > > > patch. Perhaps we should do that in the following patch, where
> > > > it needs this? Or another individual patch moving this alone?
> > > >
> > >
> > > Next patch. I just tried to point out the required change caused
> > > by next patch. 
Nicolin Chen March 15, 2023, 9:03 a.m. UTC | #13
Hi,

On Wed, Mar 15, 2023 at 06:50:53AM +0000, Tian, Kevin wrote:

> > So, this preparatory series will add a pair of simple attach()
> > and detach() APIs. Then the cdev series will add the locking
> > and the ioas_unpin stuff as a rework of the detach() API.

> > I think they can be something mingled... the sample code that
> > I sent previously could take care of those conditions. But, I
> > am also thinking a bit that maybe attach() does not need the
> > locking? I can do a separate replace() function in this case.
> >
> 
> w/o locking then you need smp_store_release() and its pair.
> 
> anyway it's not in perf critical path. Keeping lock for attach
> is simpler and safe.

OK. Basically I followed what Jason suggested by having three
APIs and combined Kevin's inputs about the difference between
the attach/replace(). I also updated the replace changes, and
rebased all nesting (infrastructure, VT-d and SMMU):
https://github.com/nicolinc/iommufd/commits/wip/iommufd_nesting-03142023

The major three changes for those APIs:
[1] This adds iommufd_access_attach() in this series:
    "iommufd: Create access in vfio_iommufd_emulated_bind()"
    https://github.com/nicolinc/iommufd/commit/34fba7509429380f828fb23dcca5ceaeb40e22b5
[2] This adds iommufd_access_detach() in the cdev series:
    "iommufd/device: Add iommufd_access_detach() API"
    https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a097e2c9d9e26af4
[3] This adds iommufd_access_replace() in the replace series:
    "iommufd: Add iommufd_access_replace() API"
    https://github.com/nicolinc/iommufd/commit/36507fa9f0f42cf1a5bebe7c9bc2bf319b7654a8

Please check if they look okay, so that Yi can integrate them
accordingly to the emulated/cdev series.

[*] This is the patch that I posted in the other mail addressing
    Kevin's comments on iommufd_ctx_get():
    "iommufd/device: Do iommufd_ctx_get() at the top of iommufd_access_create()"
    https://github.com/nicolinc/iommufd/commit/077b09bb83329dc046753f4ef672f5bf6386755c
    (I just saw Yi's reply concerning its necessity. Feel free
     to drop in that case.)

Thanks
Nicolin

P.S. Attaching the list of changes with their locations:
3791dedf98e8 cover-letter: Add IO page table replacement support
c8ebf51c3c9b vfio: Support IO page table replacement
c5710f23e8f6 iommufd/selftest: Add IOMMU_TEST_OP_ACCESS_REPLACE_IOAS coverage
[3] 36507fa9f0f4 iommufd: Add iommufd_access_replace() API
0263855d1e8b vfio: Do not allow !ops->dma_unmap in vfio_pin/unpin_pages()
e39ed55e77a0 cover-letter: Add vfio_device cdev for iommufd support
26fd7fccaef3 docs: vfio: Add vfio device cdev description
f10f3e3162bb vfio: Compile group optionally
9588ae4c4049 vfio: Add VFIO_DEVICE_AT[DE]TACH_IOMMUFD_PT
3e57108eac64 vfio: Add VFIO_DEVICE_BIND_IOMMUFD
b925716dd226 vfio: Add cdev for vfio_device
db309463ab92 vfio-iommufd: Add detach_ioas support for emulated VFIO devices
[2] 4110522146ca iommufd/device: Add iommufd_access_detach() API
abca7e1e063a vfio-iommufd: Add detach_ioas support for physical VFIO devices
9d368f7247c7 vfio: Record devid in vfio_device_file
683af0a471e1 vfio-iommufd: Split the compat_ioas attach out from vfio_iommufd_bind()
32a2e7de1d53 vfio-iommufd: Split the no-iommu support out from vfio_iommufd_bind()
8a1c042379f5 vfio: Make vfio_device_first_open() to accept NULL iommufd for noiommu
fc6e0ed2aa44 vfio: Make vfio_device_open() single open for device cdev path
3f6821d507a4 vfio: Add cdev_device_open_cnt to vfio_group
896cde40a016 vfio: Block device access via device fd until device is opened
f422c4216a19 vfio: Pass struct vfio_device_file * to vfio_device_open/close()
b187f9980fed kvm/vfio: Accept vfio device file from userspace
721e2e60ff54 kvm/vfio: Rename kvm_vfio_group to prepare for accepting vfio device fd
8993c4c75c20 vfio: Accept vfio device file in the KVM facing kAPI
a92c45ae0ce6 vfio: Remove vfio_file_is_group()
fb586f783934 vfio: Refine vfio file kAPIs for KVM
50694af6f3c0 vfio: Allocate per device file structure
df21c0737eef cover-letter: Make vfio-pci hot reset prepared for vfio device cdev
5c25c874d7e0 vfio/pci: Accept device fd in VFIO_DEVICE_PCI_HOT_RESET ioctl
7c30ce8b54db vfio: Accpet device file from vfio PCI hot reset path
e3209342db44 vfio: Refine vfio file kAPIs for vfio PCI hot reset
8354fd79944e vfio/pci: Rename the helpers and data in hot reset path to accept device fd
54387efb858c vfio/pci: Allow passing zero-length fd array in VFIO_DEVICE_PCI_HOT_RESET
cd93ffb62c51 vfio/pci: Only need to check opened devices in the dev_set for hot reset
2a6fd7231cbf vfio/pci: Update comment around group_fd get in vfio_pci_ioctl_pci_hot_reset()
480abea5961e cover-letter: vfio: Make emulated devices prepared for vfio device cdev
46b6d1ae1754 vfio: Check the presence for iommufd callbacks in __vfio_register_dev()
6064b9f81817 Samples/mdev: Uses the vfio emulated iommufd ops set in the mdev sample drivers
c20852af7291 vfio-iommufd: Make vfio_iommufd_emulated_bind() return iommufd_access ID
3405865b0b3f vfio-iommufd: No need to record iommufd_ctx in vfio_device
[*] 077b09bb8332 iommufd/device: Do iommufd_ctx_get() at the top of iommufd_access_create()
[1] 34fba7509429 iommufd: Create access in vfio_iommufd_emulated_bind()
a5d8ac47554f docs: kvm: vfio: Require call KVM_DEV_VFIO_GROUP_ADD before VFIO_GROUP_GET_DEVICE_FD
Yi Liu March 15, 2023, 12:18 p.m. UTC | #14
> From: Nicolin Chen <nicolinc@nvidia.com>
> Sent: Wednesday, March 15, 2023 5:03 PM
> 
> Hi,
> 
> On Wed, Mar 15, 2023 at 06:50:53AM +0000, Tian, Kevin wrote:
> 
> > > So, this preparatory series will add a pair of simple attach()
> > > and detach() APIs. Then the cdev series will add the locking
> > > and the ioas_unpin stuff as a rework of the detach() API.
> 
> > > I think they can be something mingled... the sample code that
> > > I sent previously could take care of those conditions. But, I
> > > am also thinking a bit that maybe attach() does not need the
> > > locking? I can do a separate replace() function in this case.
> > >
> >
> > w/o locking then you need smp_store_release() and its pair.
> >
> > anyway it's not in perf critical path. Keeping lock for attach
> > is simpler and safe.
> 
> OK. Basically I followed what Jason suggested by having three
> APIs and combined Kevin's inputs about the difference between
> the attach/replace(). I also updated the replace changes, and
> rebased all nesting (infrastructure, VT-d and SMMU):
> https://github.com/nicolinc/iommufd/commits/wip/iommufd_nesting-03142023
> 
> The major three changes for those APIs:
> [1] This adds iommufd_access_attach() in this series:
>     "iommufd: Create access in vfio_iommufd_emulated_bind()"
> 
> https://github.com/nicolinc/iommufd/commit/34fba7509429380f828fb23dc
> ca5ceaeb40e22b5
> [2] This adds iommufd_access_detach() in the cdev series:
>     "iommufd/device: Add iommufd_access_detach() API"
> 
> https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> 097e2c9d9e26af4
> [3] This adds iommufd_access_replace() in the replace series:
>     "iommufd: Add iommufd_access_replace() API"
> 
> https://github.com/nicolinc/iommufd/commit/36507fa9f0f42cf1a5bebe7c9
> bc2bf319b7654a8
> 
> Please check if they look okay, so that Yi can integrate them
> accordingly to the emulated/cdev series.

Thanks. I'll start to integrate after ack from Kevin or Jason. btw.
Below is my latest code (rebased on top of rc-2). 
Tian, Kevin March 16, 2023, 12:17 a.m. UTC | #15
> From: Liu, Yi L <yi.l.liu@intel.com>
> Sent: Wednesday, March 15, 2023 4:53 PM
> 
> > From: Tian, Kevin <kevin.tian@intel.com>
> > Sent: Wednesday, March 15, 2023 2:52 PM
> >
> > > From: Nicolin Chen
> > > Sent: Wednesday, March 15, 2023 2:22 PM
> > >
> > > On Wed, Mar 15, 2023 at 06:16:37AM +0000, Tian, Kevin wrote:
> > > > External email: Use caution opening links or attachments
> > > >
> > > >
> > > > > From: Nicolin Chen
> > > > > Sent: Wednesday, March 15, 2023 2:51 AM
> > > > >
> > > > > On Fri, Mar 10, 2023 at 02:08:15AM +0000, Tian, Kevin wrote:
> > > > > > External email: Use caution opening links or attachments
> > > > > >
> > > > > >
> > > > > > > From: Liu, Yi L <yi.l.liu@intel.com>
> > > > > > > Sent: Wednesday, March 8, 2023 9:14 PM
> > > > > > >
> > > > > > > @@ -449,33 +450,18 @@ iommufd_access_create(struct
> > > iommufd_ctx
> > > > > *ictx,
> > > > > > > u32 ioas_id,
> > > > > > >       access->data = data;
> > > > > > >       access->ops = ops;
> > > > > > >
> > > > > > > -     obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
> > > > > > > -     if (IS_ERR(obj)) {
> > > > > > > -             rc = PTR_ERR(obj);
> > > > > > > -             goto out_abort;
> > > > > > > -     }
> > > > > > > -     access->ioas = container_of(obj, struct iommufd_ioas, obj);
> > > > > > > -     iommufd_ref_to_users(obj);
> > > > > > > -
> > > > > > >       if (ops->needs_pin_pages)
> > > > > > >               access->iova_alignment = PAGE_SIZE;
> > > > > > >       else
> > > > > > >               access->iova_alignment = 1;
> > > > > > > -     rc = iopt_add_access(&access->ioas->iopt, access);
> > > > > > > -     if (rc)
> > > > > > > -             goto out_put_ioas;
> > > > > > >
> > > > > > >       /* The calling driver is a user until iommufd_access_destroy()
> */
> > > > > > >       refcount_inc(&access->obj.users);
> > > > > > > +     mutex_init(&access->ioas_lock);
> > > > > > >       access->ictx = ictx;
> > > > > > >       iommufd_ctx_get(ictx);
> > > > > >
> > > > > > this refcnt get should be moved to the start given next patch
> > > > > > removes the reference in the caller side.
> 
> This change is ok but seems not necessary.
> 
> Yes, vfio_iommufd_emulated_bind() will not have reference on the
> ictx after the next patch. However, it gets reference only because it
> wants to store it in vfio_device. Now, it does not store it. So no get.
> I think the caller of vfio_iommufd_emulated_bind() should ensure
> the ictx is valid. Also check the physical device bind. So maybe not
> necessary to get ictx before calling iommufd_access_create(). This is
> the same with the vfio_iommufd_physical_bind() which calls
> iommufd_device_bind() without ictx get, and iommufd_device_bind()
> also gets ictx in the end.
> 

You are right. I overlooked the fact that ictx is already held by the
caller of bind.
Nicolin Chen March 16, 2023, 12:28 a.m. UTC | #16
On Thu, Mar 16, 2023 at 12:17:11AM +0000, Tian, Kevin wrote:

> > > > > > > > @@ -449,33 +450,18 @@ iommufd_access_create(struct
> > > > iommufd_ctx

> > > > > > > >       refcount_inc(&access->obj.users);
> > > > > > > > +     mutex_init(&access->ioas_lock);
> > > > > > > >       access->ictx = ictx;
> > > > > > > >       iommufd_ctx_get(ictx);
> > > > > > >
> > > > > > > this refcnt get should be moved to the start given next patch
> > > > > > > removes the reference in the caller side.
> >
> > This change is ok but seems not necessary.
> >
> > Yes, vfio_iommufd_emulated_bind() will not have reference on the
> > ictx after the next patch. However, it gets reference only because it
> > wants to store it in vfio_device. Now, it does not store it. So no get.
> > I think the caller of vfio_iommufd_emulated_bind() should ensure
> > the ictx is valid. Also check the physical device bind. So maybe not
> > necessary to get ictx before calling iommufd_access_create(). This is
> > the same with the vfio_iommufd_physical_bind() which calls
> > iommufd_device_bind() without ictx get, and iommufd_device_bind()
> > also gets ictx in the end.
> >
> 
> You are right. I overlooked the fact that ictx is already held by the
> caller of bind.

I am dropping it then :)

Nic
Nicolin Chen March 16, 2023, 12:32 a.m. UTC | #17
On Wed, Mar 15, 2023 at 12:18:01PM +0000, Liu, Yi L wrote:
> > OK. Basically I followed what Jason suggested by having three
> > APIs and combined Kevin's inputs about the difference between
> > the attach/replace(). I also updated the replace changes, and
> > rebased all nesting (infrastructure, VT-d and SMMU):
> > https://github.com/nicolinc/iommufd/commits/wip/iommufd_nesting-03142023
> >
> > The major three changes for those APIs:
> > [1] This adds iommufd_access_attach() in this series:
> >     "iommufd: Create access in vfio_iommufd_emulated_bind()"
> >
> > https://github.com/nicolinc/iommufd/commit/34fba7509429380f828fb23dc
> > ca5ceaeb40e22b5
> > [2] This adds iommufd_access_detach() in the cdev series:
> >     "iommufd/device: Add iommufd_access_detach() API"
> >
> > https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> > 097e2c9d9e26af4
> > [3] This adds iommufd_access_replace() in the replace series:
> >     "iommufd: Add iommufd_access_replace() API"
> >
> > https://github.com/nicolinc/iommufd/commit/36507fa9f0f42cf1a5bebe7c9
> > bc2bf319b7654a8
> >
> > Please check if they look okay, so that Yi can integrate them
> > accordingly to the emulated/cdev series.
> 
> Thanks. I'll start to integrate after ack from Kevin or Jason. btw.
> Below is my latest code (rebased on top of rc-2). 
Tian, Kevin March 16, 2023, 2:53 a.m. UTC | #18
> From: Nicolin Chen <nicolinc@nvidia.com>
> Sent: Wednesday, March 15, 2023 5:03 PM
> 
> Hi,
> 
> On Wed, Mar 15, 2023 at 06:50:53AM +0000, Tian, Kevin wrote:
> 
> > > So, this preparatory series will add a pair of simple attach()
> > > and detach() APIs. Then the cdev series will add the locking
> > > and the ioas_unpin stuff as a rework of the detach() API.
> 
> > > I think they can be something mingled... the sample code that
> > > I sent previously could take care of those conditions. But, I
> > > am also thinking a bit that maybe attach() does not need the
> > > locking? I can do a separate replace() function in this case.
> > >
> >
> > w/o locking then you need smp_store_release() and its pair.
> >
> > anyway it's not in perf critical path. Keeping lock for attach
> > is simpler and safe.
> 
> OK. Basically I followed what Jason suggested by having three
> APIs and combined Kevin's inputs about the difference between
> the attach/replace(). I also updated the replace changes, and
> rebased all nesting (infrastructure, VT-d and SMMU):
> https://github.com/nicolinc/iommufd/commits/wip/iommufd_nesting-
> 03142023
> 
> The major three changes for those APIs:
> [1] This adds iommufd_access_attach() in this series:
>     "iommufd: Create access in vfio_iommufd_emulated_bind()"
> 
> https://github.com/nicolinc/iommufd/commit/34fba7509429380f828fb23dcc
> a5ceaeb40e22b5

WARN_ON(!vdev->iommufd_access) in vfio_iommufd_emulated_attach_ioas()

> [2] This adds iommufd_access_detach() in the cdev series:
>     "iommufd/device: Add iommufd_access_detach() API"
> 
> https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> 097e2c9d9e26af4

also add a check if old_ioas exists it must equal to the new_ioas in attach.

> [3] This adds iommufd_access_replace() in the replace series:
>     "iommufd: Add iommufd_access_replace() API"
> 
> https://github.com/nicolinc/iommufd/commit/36507fa9f0f42cf1a5bebe7c9b
> c2bf319b7654a8

lockdep_assert_held(&access->ioas_lock) in iommufd_access_change_pt()

cur_ioas is uninitialized in iommufd_access_change_pt(). Actually we don't
need an extra variable given only one reference to access->ioas.

similarly directly refer to access->ioas in iommufd_access_replace().

> 
> Please check if they look okay, so that Yi can integrate them
> accordingly to the emulated/cdev series.

this split looks reasonable to me. Go ahead.
Nicolin Chen March 16, 2023, 3:25 a.m. UTC | #19
On Thu, Mar 16, 2023 at 02:53:50AM +0000, Tian, Kevin wrote:

> > Please check if they look okay, so that Yi can integrate them
> > accordingly to the emulated/cdev series.
> 
> this split looks reasonable to me. Go ahead.

Thanks for the review! I will address those comments and renew
those commits by the end of the day.

Thanks
Nic
Nicolin Chen March 16, 2023, 5:33 a.m. UTC | #20
Hi Kevin,

I've fixed the other two commits. Here is the one that I am
not sure about:

On Thu, Mar 16, 2023 at 02:53:50AM +0000, Tian, Kevin wrote:

> > [2] This adds iommufd_access_detach() in the cdev series:
> >     "iommufd/device: Add iommufd_access_detach() API"
> >
> > https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> > 097e2c9d9e26af4
> 
> also add a check if old_ioas exists it must equal to the new_ioas in attach.

This is the commit adding detach(). And there's a check in it:
	if (WARN_ON(!access->ioas))

Do you mean having an "if (access->ioas) return -EBUSY;" line
in the commit adding attach()?

And, how should we check in the detach() if it equals to the
new_ioas in attach? Isn't the WARN_ON(!access->ioas) enough?

Thanks
Nic
Tian, Kevin March 16, 2023, 5:38 a.m. UTC | #21
> From: Nicolin Chen <nicolinc@nvidia.com>
> Sent: Thursday, March 16, 2023 1:33 PM
> 
> Hi Kevin,
> 
> I've fixed the other two commits. Here is the one that I am
> not sure about:
> 
> On Thu, Mar 16, 2023 at 02:53:50AM +0000, Tian, Kevin wrote:
> 
> > > [2] This adds iommufd_access_detach() in the cdev series:
> > >     "iommufd/device: Add iommufd_access_detach() API"
> > >
> > >
> https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> > > 097e2c9d9e26af4
> >
> > also add a check if old_ioas exists it must equal to the new_ioas in attach.
> 
> This is the commit adding detach(). And there's a check in it:
> 	if (WARN_ON(!access->ioas))
> 
> Do you mean having an "if (access->ioas) return -EBUSY;" line
> in the commit adding attach()?

if (access->ioas && access->ioas != new_ioas)
	return -EBUSY;

yes this is for attach. 

> 
> And, how should we check in the detach() if it equals to the
> new_ioas in attach? Isn't the WARN_ON(!access->ioas) enough?
> 

this is not required.
Nicolin Chen March 16, 2023, 5:43 a.m. UTC | #22
On Thu, Mar 16, 2023 at 05:38:41AM +0000, Tian, Kevin wrote:
> External email: Use caution opening links or attachments
> 
> 
> > From: Nicolin Chen <nicolinc@nvidia.com>
> > Sent: Thursday, March 16, 2023 1:33 PM
> >
> > Hi Kevin,
> >
> > I've fixed the other two commits. Here is the one that I am
> > not sure about:
> >
> > On Thu, Mar 16, 2023 at 02:53:50AM +0000, Tian, Kevin wrote:
> >
> > > > [2] This adds iommufd_access_detach() in the cdev series:
> > > >     "iommufd/device: Add iommufd_access_detach() API"
> > > >
> > > >
> > https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> > > > 097e2c9d9e26af4
> > >
> > > also add a check if old_ioas exists it must equal to the new_ioas in attach.
> >
> > This is the commit adding detach(). And there's a check in it:
> >       if (WARN_ON(!access->ioas))
> >
> > Do you mean having an "if (access->ioas) return -EBUSY;" line
> > in the commit adding attach()?
> 
> if (access->ioas && access->ioas != new_ioas)
>         return -EBUSY;
> 
> yes this is for attach.

OK. For attach(), the access->ioas shouldn't be !NULL, I think.
At the point of adding attach(), the uAPI doesn't support the
replacement use case yet. And later we have a separate API for
that.

So I think it'd be just:
	if (access->ioas)
		return -EBUSY;

The reason why I didn't add it is actually because the caller
vfio_iommufd_emulated_attach_ioas() has a check of "attached"
already. Yet, it doesn't hurt to have one more in the API.

Thanks
Nic
Tian, Kevin March 16, 2023, 5:49 a.m. UTC | #23
> From: Nicolin Chen
> Sent: Thursday, March 16, 2023 1:44 PM
> 
> On Thu, Mar 16, 2023 at 05:38:41AM +0000, Tian, Kevin wrote:
> > External email: Use caution opening links or attachments
> >
> >
> > > From: Nicolin Chen <nicolinc@nvidia.com>
> > > Sent: Thursday, March 16, 2023 1:33 PM
> > >
> > > Hi Kevin,
> > >
> > > I've fixed the other two commits. Here is the one that I am
> > > not sure about:
> > >
> > > On Thu, Mar 16, 2023 at 02:53:50AM +0000, Tian, Kevin wrote:
> > >
> > > > > [2] This adds iommufd_access_detach() in the cdev series:
> > > > >     "iommufd/device: Add iommufd_access_detach() API"
> > > > >
> > > > >
> > >
> https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> > > > > 097e2c9d9e26af4
> > > >
> > > > also add a check if old_ioas exists it must equal to the new_ioas in
> attach.
> > >
> > > This is the commit adding detach(). And there's a check in it:
> > >       if (WARN_ON(!access->ioas))
> > >
> > > Do you mean having an "if (access->ioas) return -EBUSY;" line
> > > in the commit adding attach()?
> >
> > if (access->ioas && access->ioas != new_ioas)
> >         return -EBUSY;
> >
> > yes this is for attach.
> 
> OK. For attach(), the access->ioas shouldn't be !NULL, I think.
> At the point of adding attach(), the uAPI doesn't support the
> replacement use case yet. And later we have a separate API for
> that.

what about user calling attach twice in cdev?

> 
> So I think it'd be just:
> 	if (access->ioas)
> 		return -EBUSY;
> 
> The reason why I didn't add it is actually because the caller
> vfio_iommufd_emulated_attach_ioas() has a check of "attached"
> already. Yet, it doesn't hurt to have one more in the API.
> 

but here the slight difference is that in physical path we allow
attach twice to the same hwpt. they should be consistent:

	if (idev->igroup->hwpt != NULL && idev->igroup->hwpt != hwpt)
		return -EINVAL;
Nicolin Chen March 16, 2023, 5:56 a.m. UTC | #24
On Thu, Mar 16, 2023 at 05:49:20AM +0000, Tian, Kevin wrote:
> External email: Use caution opening links or attachments
> 
> 
> > From: Nicolin Chen
> > Sent: Thursday, March 16, 2023 1:44 PM
> >
> > On Thu, Mar 16, 2023 at 05:38:41AM +0000, Tian, Kevin wrote:
> > > External email: Use caution opening links or attachments
> > >
> > >
> > > > From: Nicolin Chen <nicolinc@nvidia.com>
> > > > Sent: Thursday, March 16, 2023 1:33 PM
> > > >
> > > > Hi Kevin,
> > > >
> > > > I've fixed the other two commits. Here is the one that I am
> > > > not sure about:
> > > >
> > > > On Thu, Mar 16, 2023 at 02:53:50AM +0000, Tian, Kevin wrote:
> > > >
> > > > > > [2] This adds iommufd_access_detach() in the cdev series:
> > > > > >     "iommufd/device: Add iommufd_access_detach() API"
> > > > > >
> > > > > >
> > > >
> > https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> > > > > > 097e2c9d9e26af4
> > > > >
> > > > > also add a check if old_ioas exists it must equal to the new_ioas in
> > attach.
> > > >
> > > > This is the commit adding detach(). And there's a check in it:
> > > >       if (WARN_ON(!access->ioas))
> > > >
> > > > Do you mean having an "if (access->ioas) return -EBUSY;" line
> > > > in the commit adding attach()?
> > >
> > > if (access->ioas && access->ioas != new_ioas)
> > >         return -EBUSY;
> > >
> > > yes this is for attach.
> >
> > OK. For attach(), the access->ioas shouldn't be !NULL, I think.
> > At the point of adding attach(), the uAPI doesn't support the
> > replacement use case yet. And later we have a separate API for
> > that.
> 
> what about user calling attach twice in cdev?
> 
> >
> > So I think it'd be just:
> >       if (access->ioas)
> >               return -EBUSY;
> >
> > The reason why I didn't add it is actually because the caller
> > vfio_iommufd_emulated_attach_ioas() has a check of "attached"
> > already. Yet, it doesn't hurt to have one more in the API.
> >
> 
> but here the slight difference is that in physical path we allow
> attach twice to the same hwpt. they should be consistent:
> 
>         if (idev->igroup->hwpt != NULL && idev->igroup->hwpt != hwpt)
>                 return -EINVAL;

I see. The point is to support duplicated calls:
	ATTACH (pt_id = ioas1)
	ATTACH (pt_id = ioas1)

Then I will add this to keep the consistency:
	if (access->ioas != NULL && access->ioas != new_ioas)
		return -EINVAL;

Thanks
Nic
Yi Liu March 16, 2023, 6:01 a.m. UTC | #25
> From: Tian, Kevin <kevin.tian@intel.com>
> Sent: Thursday, March 16, 2023 1:49 PM
> 
> > From: Nicolin Chen
> > Sent: Thursday, March 16, 2023 1:44 PM
> >
> > On Thu, Mar 16, 2023 at 05:38:41AM +0000, Tian, Kevin wrote:
> > > External email: Use caution opening links or attachments
> > >
> > >
> > > > From: Nicolin Chen <nicolinc@nvidia.com>
> > > > Sent: Thursday, March 16, 2023 1:33 PM
> > > >
> > > > Hi Kevin,
> > > >
> > > > I've fixed the other two commits. Here is the one that I am
> > > > not sure about:
> > > >
> > > > On Thu, Mar 16, 2023 at 02:53:50AM +0000, Tian, Kevin wrote:
> > > >
> > > > > > [2] This adds iommufd_access_detach() in the cdev series:
> > > > > >     "iommufd/device: Add iommufd_access_detach() API"
> > > > > >
> > > > > >
> > > >
> >
> https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a
> > > > > > 097e2c9d9e26af4
> > > > >
> > > > > also add a check if old_ioas exists it must equal to the new_ioas in
> > attach.
> > > >
> > > > This is the commit adding detach(). And there's a check in it:
> > > >       if (WARN_ON(!access->ioas))
> > > >
> > > > Do you mean having an "if (access->ioas) return -EBUSY;" line
> > > > in the commit adding attach()?
> > >
> > > if (access->ioas && access->ioas != new_ioas)
> > >         return -EBUSY;
> > >
> > > yes this is for attach.
> >
> > OK. For attach(), the access->ioas shouldn't be !NULL, I think.
> > At the point of adding attach(), the uAPI doesn't support the
> > replacement use case yet. And later we have a separate API for
> > that.
> 
> what about user calling attach twice in cdev?

In the cdev series, VFIO only one attach is allowed so far. If
vfio_device->iommufd_attached is set, would return -EBUSY to user.
But this does not prevent the iommufd side to allow attach twice.
Nic's replace series would relax it if user means to replace existing
attached hwpt/ioas.

Regards,
Yi Liu
Jason Gunthorpe March 20, 2023, 2:49 p.m. UTC | #26
On Wed, Mar 15, 2023 at 02:03:09AM -0700, Nicolin Chen wrote:
> Hi,
> 
> On Wed, Mar 15, 2023 at 06:50:53AM +0000, Tian, Kevin wrote:
> 
> > > So, this preparatory series will add a pair of simple attach()
> > > and detach() APIs. Then the cdev series will add the locking
> > > and the ioas_unpin stuff as a rework of the detach() API.
> 
> > > I think they can be something mingled... the sample code that
> > > I sent previously could take care of those conditions. But, I
> > > am also thinking a bit that maybe attach() does not need the
> > > locking? I can do a separate replace() function in this case.
> > >
> > 
> > w/o locking then you need smp_store_release() and its pair.
> > 
> > anyway it's not in perf critical path. Keeping lock for attach
> > is simpler and safe.
> 
> OK. Basically I followed what Jason suggested by having three
> APIs and combined Kevin's inputs about the difference between
> the attach/replace(). I also updated the replace changes, and
> rebased all nesting (infrastructure, VT-d and SMMU):
> https://github.com/nicolinc/iommufd/commits/wip/iommufd_nesting-03142023
> 
> The major three changes for those APIs:
> [1] This adds iommufd_access_attach() in this series:
>     "iommufd: Create access in vfio_iommufd_emulated_bind()"
>     https://github.com/nicolinc/iommufd/commit/34fba7509429380f828fb23dcca5ceaeb40e22b5
> [2] This adds iommufd_access_detach() in the cdev series:
>     "iommufd/device: Add iommufd_access_detach() API"
>     https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a097e2c9d9e26af4
> [3] This adds iommufd_access_replace() in the replace series:
>     "iommufd: Add iommufd_access_replace() API"
>     https://github.com/nicolinc/iommufd/commit/36507fa9f0f42cf1a5bebe7c9bc2bf319b7654a8
> 
> Please check if they look okay, so that Yi can integrate them
> accordingly to the emulated/cdev series.

I don't understand why this is being put in front of the cdev series?

Jason
Yi Liu March 20, 2023, 3:11 p.m. UTC | #27
On 2023/3/20 22:49, Jason Gunthorpe wrote:
> On Wed, Mar 15, 2023 at 02:03:09AM -0700, Nicolin Chen wrote:
>> Hi,
>>
>> On Wed, Mar 15, 2023 at 06:50:53AM +0000, Tian, Kevin wrote:
>>
>>>> So, this preparatory series will add a pair of simple attach()
>>>> and detach() APIs. Then the cdev series will add the locking
>>>> and the ioas_unpin stuff as a rework of the detach() API.
>>
>>>> I think they can be something mingled... the sample code that
>>>> I sent previously could take care of those conditions. But, I
>>>> am also thinking a bit that maybe attach() does not need the
>>>> locking? I can do a separate replace() function in this case.
>>>>
>>>
>>> w/o locking then you need smp_store_release() and its pair.
>>>
>>> anyway it's not in perf critical path. Keeping lock for attach
>>> is simpler and safe.
>>
>> OK. Basically I followed what Jason suggested by having three
>> APIs and combined Kevin's inputs about the difference between
>> the attach/replace(). I also updated the replace changes, and
>> rebased all nesting (infrastructure, VT-d and SMMU):
>> https://github.com/nicolinc/iommufd/commits/wip/iommufd_nesting-03142023
>>
>> The major three changes for those APIs:
>> [1] This adds iommufd_access_attach() in this series:
>>      "iommufd: Create access in vfio_iommufd_emulated_bind()"
>>      https://github.com/nicolinc/iommufd/commit/34fba7509429380f828fb23dcca5ceaeb40e22b5
>> [2] This adds iommufd_access_detach() in the cdev series:
>>      "iommufd/device: Add iommufd_access_detach() API"
>>      https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a097e2c9d9e26af4
>> [3] This adds iommufd_access_replace() in the replace series:
>>      "iommufd: Add iommufd_access_replace() API"
>>      https://github.com/nicolinc/iommufd/commit/36507fa9f0f42cf1a5bebe7c9bc2bf319b7654a8
>>
>> Please check if they look okay, so that Yi can integrate them
>> accordingly to the emulated/cdev series.
> 
> I don't understand why this is being put in front of the cdev series?

because we want to make emulated devices have iommufd_access in the
bind, then it can return iommufd_access->obj.id to userspace when
adding cdev.
Jason Gunthorpe March 20, 2023, 3:33 p.m. UTC | #28
On Mon, Mar 20, 2023 at 11:11:51PM +0800, Yi Liu wrote:
> 
> 
> On 2023/3/20 22:49, Jason Gunthorpe wrote:
> > On Wed, Mar 15, 2023 at 02:03:09AM -0700, Nicolin Chen wrote:
> > > Hi,
> > > 
> > > On Wed, Mar 15, 2023 at 06:50:53AM +0000, Tian, Kevin wrote:
> > > 
> > > > > So, this preparatory series will add a pair of simple attach()
> > > > > and detach() APIs. Then the cdev series will add the locking
> > > > > and the ioas_unpin stuff as a rework of the detach() API.
> > > 
> > > > > I think they can be something mingled... the sample code that
> > > > > I sent previously could take care of those conditions. But, I
> > > > > am also thinking a bit that maybe attach() does not need the
> > > > > locking? I can do a separate replace() function in this case.
> > > > > 
> > > > 
> > > > w/o locking then you need smp_store_release() and its pair.
> > > > 
> > > > anyway it's not in perf critical path. Keeping lock for attach
> > > > is simpler and safe.
> > > 
> > > OK. Basically I followed what Jason suggested by having three
> > > APIs and combined Kevin's inputs about the difference between
> > > the attach/replace(). I also updated the replace changes, and
> > > rebased all nesting (infrastructure, VT-d and SMMU):
> > > https://github.com/nicolinc/iommufd/commits/wip/iommufd_nesting-03142023
> > > 
> > > The major three changes for those APIs:
> > > [1] This adds iommufd_access_attach() in this series:
> > >      "iommufd: Create access in vfio_iommufd_emulated_bind()"
> > >      https://github.com/nicolinc/iommufd/commit/34fba7509429380f828fb23dcca5ceaeb40e22b5
> > > [2] This adds iommufd_access_detach() in the cdev series:
> > >      "iommufd/device: Add iommufd_access_detach() API"
> > >      https://github.com/nicolinc/iommufd/commit/4110522146ca1fc0d5321c04a097e2c9d9e26af4
> > > [3] This adds iommufd_access_replace() in the replace series:
> > >      "iommufd: Add iommufd_access_replace() API"
> > >      https://github.com/nicolinc/iommufd/commit/36507fa9f0f42cf1a5bebe7c9bc2bf319b7654a8
> > > 
> > > Please check if they look okay, so that Yi can integrate them
> > > accordingly to the emulated/cdev series.
> > 
> > I don't understand why this is being put in front of the cdev series?
> 
> because we want to make emulated devices have iommufd_access in the
> bind, then it can return iommufd_access->obj.id to userspace when
> adding cdev.

Ah OK

Jason
diff mbox series

Patch

diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index a0c66f47a65a..71c4d38994b3 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -412,9 +412,12 @@  void iommufd_access_destroy_object(struct iommufd_object *obj)
 	struct iommufd_access *access =
 		container_of(obj, struct iommufd_access, obj);
 
-	iopt_remove_access(&access->ioas->iopt, access);
+	if (access->ioas) {
+		iopt_remove_access(&access->ioas->iopt, access);
+		refcount_dec(&access->ioas->obj.users);
+	}
 	iommufd_ctx_put(access->ictx);
-	refcount_dec(&access->ioas->obj.users);
+	mutex_destroy(&access->ioas_lock);
 }
 
 /**
@@ -431,12 +434,10 @@  void iommufd_access_destroy_object(struct iommufd_object *obj)
  * The provided ops are required to use iommufd_access_pin_pages().
  */
 struct iommufd_access *
-iommufd_access_create(struct iommufd_ctx *ictx, u32 ioas_id,
+iommufd_access_create(struct iommufd_ctx *ictx,
 		      const struct iommufd_access_ops *ops, void *data)
 {
 	struct iommufd_access *access;
-	struct iommufd_object *obj;
-	int rc;
 
 	/*
 	 * There is no uAPI for the access object, but to keep things symmetric
@@ -449,33 +450,18 @@  iommufd_access_create(struct iommufd_ctx *ictx, u32 ioas_id,
 	access->data = data;
 	access->ops = ops;
 
-	obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
-	if (IS_ERR(obj)) {
-		rc = PTR_ERR(obj);
-		goto out_abort;
-	}
-	access->ioas = container_of(obj, struct iommufd_ioas, obj);
-	iommufd_ref_to_users(obj);
-
 	if (ops->needs_pin_pages)
 		access->iova_alignment = PAGE_SIZE;
 	else
 		access->iova_alignment = 1;
-	rc = iopt_add_access(&access->ioas->iopt, access);
-	if (rc)
-		goto out_put_ioas;
 
 	/* The calling driver is a user until iommufd_access_destroy() */
 	refcount_inc(&access->obj.users);
+	mutex_init(&access->ioas_lock);
 	access->ictx = ictx;
 	iommufd_ctx_get(ictx);
 	iommufd_object_finalize(ictx, &access->obj);
 	return access;
-out_put_ioas:
-	refcount_dec(&access->ioas->obj.users);
-out_abort:
-	iommufd_object_abort(ictx, &access->obj);
-	return ERR_PTR(rc);
 }
 EXPORT_SYMBOL_NS_GPL(iommufd_access_create, IOMMUFD);
 
@@ -494,6 +480,50 @@  void iommufd_access_destroy(struct iommufd_access *access)
 }
 EXPORT_SYMBOL_NS_GPL(iommufd_access_destroy, IOMMUFD);
 
+int iommufd_access_set_ioas(struct iommufd_access *access, u32 ioas_id)
+{
+	struct iommufd_ioas *new_ioas = NULL, *cur_ioas;
+	struct iommufd_ctx *ictx = access->ictx;
+	struct iommufd_object *obj;
+	int rc = 0;
+
+	if (ioas_id) {
+		obj = iommufd_get_object(ictx, ioas_id, IOMMUFD_OBJ_IOAS);
+		if (IS_ERR(obj))
+			return PTR_ERR(obj);
+		new_ioas = container_of(obj, struct iommufd_ioas, obj);
+	}
+
+	mutex_lock(&access->ioas_lock);
+	cur_ioas = access->ioas;
+	if (cur_ioas == new_ioas)
+		goto out_unlock;
+
+	if (new_ioas) {
+		rc = iopt_add_access(&new_ioas->iopt, access);
+		if (rc)
+			goto out_unlock;
+		iommufd_ref_to_users(obj);
+	}
+
+	if (cur_ioas) {
+		iopt_remove_access(&cur_ioas->iopt, access);
+		refcount_dec(&cur_ioas->obj.users);
+	}
+
+	access->ioas = new_ioas;
+	mutex_unlock(&access->ioas_lock);
+
+	return 0;
+
+out_unlock:
+	mutex_unlock(&access->ioas_lock);
+	if (new_ioas)
+		iommufd_put_object(obj);
+	return rc;
+}
+EXPORT_SYMBOL_NS_GPL(iommufd_access_set_ioas, IOMMUFD);
+
 /**
  * iommufd_access_notify_unmap - Notify users of an iopt to stop using it
  * @iopt: iopt to work on
@@ -544,8 +574,8 @@  void iommufd_access_notify_unmap(struct io_pagetable *iopt, unsigned long iova,
 void iommufd_access_unpin_pages(struct iommufd_access *access,
 				unsigned long iova, unsigned long length)
 {
-	struct io_pagetable *iopt = &access->ioas->iopt;
 	struct iopt_area_contig_iter iter;
+	struct io_pagetable *iopt;
 	unsigned long last_iova;
 	struct iopt_area *area;
 
@@ -553,6 +583,13 @@  void iommufd_access_unpin_pages(struct iommufd_access *access,
 	    WARN_ON(check_add_overflow(iova, length - 1, &last_iova)))
 		return;
 
+	mutex_lock(&access->ioas_lock);
+	if (!access->ioas) {
+		mutex_unlock(&access->ioas_lock);
+		return;
+	}
+	iopt = &access->ioas->iopt;
+
 	down_read(&iopt->iova_rwsem);
 	iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova)
 		iopt_area_remove_access(
@@ -562,6 +599,7 @@  void iommufd_access_unpin_pages(struct iommufd_access *access,
 				min(last_iova, iopt_area_last_iova(area))));
 	up_read(&iopt->iova_rwsem);
 	WARN_ON(!iopt_area_contig_done(&iter));
+	mutex_unlock(&access->ioas_lock);
 }
 EXPORT_SYMBOL_NS_GPL(iommufd_access_unpin_pages, IOMMUFD);
 
@@ -607,8 +645,8 @@  int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
 			     unsigned long length, struct page **out_pages,
 			     unsigned int flags)
 {
-	struct io_pagetable *iopt = &access->ioas->iopt;
 	struct iopt_area_contig_iter iter;
+	struct io_pagetable *iopt;
 	unsigned long last_iova;
 	struct iopt_area *area;
 	int rc;
@@ -623,6 +661,13 @@  int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
 	if (check_add_overflow(iova, length - 1, &last_iova))
 		return -EOVERFLOW;
 
+	mutex_lock(&access->ioas_lock);
+	if (!access->ioas) {
+		mutex_unlock(&access->ioas_lock);
+		return -ENOENT;
+	}
+	iopt = &access->ioas->iopt;
+
 	down_read(&iopt->iova_rwsem);
 	iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova) {
 		unsigned long last = min(last_iova, iopt_area_last_iova(area));
@@ -653,6 +698,7 @@  int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
 	}
 
 	up_read(&iopt->iova_rwsem);
+	mutex_unlock(&access->ioas_lock);
 	return 0;
 
 err_remove:
@@ -667,6 +713,7 @@  int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
 						  iopt_area_last_iova(area))));
 	}
 	up_read(&iopt->iova_rwsem);
+	mutex_unlock(&access->ioas_lock);
 	return rc;
 }
 EXPORT_SYMBOL_NS_GPL(iommufd_access_pin_pages, IOMMUFD);
@@ -686,8 +733,8 @@  EXPORT_SYMBOL_NS_GPL(iommufd_access_pin_pages, IOMMUFD);
 int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
 		      void *data, size_t length, unsigned int flags)
 {
-	struct io_pagetable *iopt = &access->ioas->iopt;
 	struct iopt_area_contig_iter iter;
+	struct io_pagetable *iopt;
 	struct iopt_area *area;
 	unsigned long last_iova;
 	int rc;
@@ -697,6 +744,13 @@  int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
 	if (check_add_overflow(iova, length - 1, &last_iova))
 		return -EOVERFLOW;
 
+	mutex_lock(&access->ioas_lock);
+	if (!access->ioas) {
+		mutex_unlock(&access->ioas_lock);
+		return -ENOENT;
+	}
+	iopt = &access->ioas->iopt;
+
 	down_read(&iopt->iova_rwsem);
 	iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova) {
 		unsigned long last = min(last_iova, iopt_area_last_iova(area));
@@ -723,6 +777,7 @@  int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
 		rc = -ENOENT;
 err_out:
 	up_read(&iopt->iova_rwsem);
+	mutex_unlock(&access->ioas_lock);
 	return rc;
 }
 EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 9d7f71510ca1..820251b83ae4 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -263,6 +263,7 @@  struct iommufd_access {
 	struct iommufd_object obj;
 	struct iommufd_ctx *ictx;
 	struct iommufd_ioas *ioas;
+	struct mutex ioas_lock;
 	const struct iommufd_access_ops *ops;
 	void *data;
 	unsigned long iova_alignment;
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index cfb5fe9a5e0e..db4011bdc8a9 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -571,7 +571,7 @@  static int iommufd_test_create_access(struct iommufd_ucmd *ucmd,
 	}
 
 	access = iommufd_access_create(
-		ucmd->ictx, ioas_id,
+		ucmd->ictx,
 		(flags & MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES) ?
 			&selftest_access_ops_pin :
 			&selftest_access_ops,
@@ -580,6 +580,9 @@  static int iommufd_test_create_access(struct iommufd_ucmd *ucmd,
 		rc = PTR_ERR(access);
 		goto out_put_fdno;
 	}
+	rc = iommufd_access_set_ioas(access, ioas_id);
+	if (rc)
+		goto out_destroy;
 	cmd->create_access.out_access_fd = fdno;
 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
 	if (rc)
diff --git a/drivers/vfio/iommufd.c b/drivers/vfio/iommufd.c
index db4efbd56042..1f87294c1931 100644
--- a/drivers/vfio/iommufd.c
+++ b/drivers/vfio/iommufd.c
@@ -138,10 +138,18 @@  static const struct iommufd_access_ops vfio_user_ops = {
 int vfio_iommufd_emulated_bind(struct vfio_device *vdev,
 			       struct iommufd_ctx *ictx, u32 *out_device_id)
 {
+	struct iommufd_access *user;
+
 	lockdep_assert_held(&vdev->dev_set->lock);
 
-	vdev->iommufd_ictx = ictx;
 	iommufd_ctx_get(ictx);
+	user = iommufd_access_create(ictx, &vfio_user_ops, vdev);
+	if (IS_ERR(user)) {
+		iommufd_ctx_put(ictx);
+		return PTR_ERR(user);
+	}
+	vdev->iommufd_access = user;
+	vdev->iommufd_ictx = ictx;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_bind);
@@ -161,15 +169,12 @@  EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_unbind);
 
 int vfio_iommufd_emulated_attach_ioas(struct vfio_device *vdev, u32 *pt_id)
 {
-	struct iommufd_access *user;
-
 	lockdep_assert_held(&vdev->dev_set->lock);
 
-	user = iommufd_access_create(vdev->iommufd_ictx, *pt_id, &vfio_user_ops,
-				     vdev);
-	if (IS_ERR(user))
-		return PTR_ERR(user);
-	vdev->iommufd_access = user;
-	return 0;
+	if (!vdev->iommufd_ictx)
+		return -EINVAL;
+	if (!vdev->iommufd_access)
+		return -ENOENT;
+	return iommufd_access_set_ioas(vdev->iommufd_access, *pt_id);
 }
 EXPORT_SYMBOL_GPL(vfio_iommufd_emulated_attach_ioas);
diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h
index c0b5b3ac34f1..247b11609c79 100644
--- a/include/linux/iommufd.h
+++ b/include/linux/iommufd.h
@@ -40,9 +40,10 @@  enum {
 };
 
 struct iommufd_access *
-iommufd_access_create(struct iommufd_ctx *ictx, u32 ioas_id,
+iommufd_access_create(struct iommufd_ctx *ictx,
 		      const struct iommufd_access_ops *ops, void *data);
 void iommufd_access_destroy(struct iommufd_access *access);
+int iommufd_access_set_ioas(struct iommufd_access *access, u32 ioas_id);
 
 void iommufd_ctx_get(struct iommufd_ctx *ictx);