diff mbox series

[04/14] vfio: factor out a vfio_group_find_or_alloc helper

Message ID 20210826133424.3362-5-hch@lst.de (mailing list archive)
State New, archived
Headers show
Series [01/14] vfio: Move vfio_iommu_group_get() to vfio_register_group_dev() | expand

Commit Message

Christoph Hellwig Aug. 26, 2021, 1:34 p.m. UTC
Factor out a helper to find or allocate the vfio_group to reduce the
spagetthi code in vfio_register_group_dev a little.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/vfio/vfio.c | 59 ++++++++++++++++++++++++++-------------------
 1 file changed, 34 insertions(+), 25 deletions(-)

Comments

Alex Williamson Aug. 26, 2021, 7:54 p.m. UTC | #1
On Thu, 26 Aug 2021 15:34:14 +0200
Christoph Hellwig <hch@lst.de> wrote:

> Factor out a helper to find or allocate the vfio_group to reduce the
> spagetthi code in vfio_register_group_dev a little.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
> ---
>  drivers/vfio/vfio.c | 59 ++++++++++++++++++++++++++-------------------
>  1 file changed, 34 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
> index 18e4c7906d1b3f..852fe22125520d 100644
> --- a/drivers/vfio/vfio.c
> +++ b/drivers/vfio/vfio.c
> @@ -823,10 +823,38 @@ void vfio_uninit_group_dev(struct vfio_device *device)
>  }
>  EXPORT_SYMBOL_GPL(vfio_uninit_group_dev);
>  
> +struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
> +{
> +	struct iommu_group *iommu_group;
> +	struct vfio_group *group;
> +
> +	iommu_group = vfio_iommu_group_get(dev);
> +	if (!iommu_group)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* a found vfio_group already holds a reference to the iommu_group */
> +	group = vfio_group_get_from_iommu(iommu_group);
> +	if (group)
> +		goto out_put;
> +
> +	/* a newly created vfio_group keeps the reference. */
> +	group = vfio_create_group(iommu_group);
> +	if (IS_ERR(group))
> +		goto out_put;
> +	return group;
> +
> +out_put:
> +#ifdef CONFIG_VFIO_NOIOMMU
> +	if (iommu_group_get_iommudata(iommu_group) == &noiommu)
> +		iommu_group_remove_device(dev);
> +#endif

When we get here via the first goto above, it doesn't match the code
we're removing below.  I stared at the below logic from patch 01 for a
while and came to the conclusion that the only way we take that else
branch is if the iommu_group already existed and was not created
because how else could we find a vfio group for that iommu group
otherwise?  Therefore we only put the iommu group reference without
removing the device, because we didn't add the device.

The above assumes we created the iommu group and added the device.  So
I think there needs to be a separate goto target below that's reached in
place of the first goto above.  Thanks,

Alex

> +	iommu_group_put(iommu_group);
> +	return group;
> +}
> +
>  int vfio_register_group_dev(struct vfio_device *device)
>  {
>  	struct vfio_device *existing_device;
> -	struct iommu_group *iommu_group;
>  	struct vfio_group *group;
>  
>  	/*
> @@ -836,36 +864,17 @@ int vfio_register_group_dev(struct vfio_device *device)
>  	if (!device->dev_set)
>  		vfio_assign_device_set(device, device);
>  
> -	iommu_group = vfio_iommu_group_get(device->dev);
> -	if (!iommu_group)
> -		return -EINVAL;
> -
> -	group = vfio_group_get_from_iommu(iommu_group);
> -	if (!group) {
> -		group = vfio_create_group(iommu_group);
> -		if (IS_ERR(group)) {
> -#ifdef CONFIG_VFIO_NOIOMMU
> -			if (iommu_group_get_iommudata(iommu_group) == &noiommu)
> -				iommu_group_remove_device(device->dev);
> -#endif
> -			iommu_group_put(iommu_group);
> -			return PTR_ERR(group);
> -		}
> -	} else {
> -		/*
> -		 * A found vfio_group already holds a reference to the
> -		 * iommu_group.  A created vfio_group keeps the reference.
> -		 */
> -		iommu_group_put(iommu_group);
> -	}
> +	group = vfio_group_find_or_alloc(device->dev);
> +	if (IS_ERR(group))
> +		return PTR_ERR(group);
>  
>  	existing_device = vfio_group_get_device(group, device->dev);
>  	if (existing_device) {
>  		dev_WARN(device->dev, "Device already exists on group %d\n",
> -			 iommu_group_id(iommu_group));
> +			 iommu_group_id(group->iommu_group));
>  		vfio_device_put(existing_device);
>  #ifdef CONFIG_VFIO_NOIOMMU
> -		if (iommu_group_get_iommudata(iommu_group) == &noiommu)
> +		if (iommu_group_get_iommudata(group->iommu_group) == &noiommu)
>  			iommu_group_remove_device(device->dev);
>  #endif
>  		vfio_group_put(group);
Jason Gunthorpe Aug. 26, 2021, 11:35 p.m. UTC | #2
On Thu, Aug 26, 2021 at 01:54:13PM -0600, Alex Williamson wrote:
> On Thu, 26 Aug 2021 15:34:14 +0200
> Christoph Hellwig <hch@lst.de> wrote:
> 
> > Factor out a helper to find or allocate the vfio_group to reduce the
> > spagetthi code in vfio_register_group_dev a little.
> > 
> > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
> >  drivers/vfio/vfio.c | 59 ++++++++++++++++++++++++++-------------------
> >  1 file changed, 34 insertions(+), 25 deletions(-)
> > 
> > diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
> > index 18e4c7906d1b3f..852fe22125520d 100644
> > +++ b/drivers/vfio/vfio.c
> > @@ -823,10 +823,38 @@ void vfio_uninit_group_dev(struct vfio_device *device)
> >  }
> >  EXPORT_SYMBOL_GPL(vfio_uninit_group_dev);
> >  
> > +struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
> > +{
> > +	struct iommu_group *iommu_group;
> > +	struct vfio_group *group;
> > +
> > +	iommu_group = vfio_iommu_group_get(dev);
> > +	if (!iommu_group)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	/* a found vfio_group already holds a reference to the iommu_group */
> > +	group = vfio_group_get_from_iommu(iommu_group);
> > +	if (group)
> > +		goto out_put;
> > +
> > +	/* a newly created vfio_group keeps the reference. */
> > +	group = vfio_create_group(iommu_group);
> > +	if (IS_ERR(group))
> > +		goto out_put;
> > +	return group;
> > +
> > +out_put:
> > +#ifdef CONFIG_VFIO_NOIOMMU
> > +	if (iommu_group_get_iommudata(iommu_group) == &noiommu)
> > +		iommu_group_remove_device(dev);
> > +#endif
> 
> When we get here via the first goto above, it doesn't match the code
> we're removing below. 

If we are in noiommu mode then the group is a new singleton group and
vfio_group_get_from_iommu() cannot succeed, so the out_put cannot
trigger for the noiommu path.

This is all improved in patch 6 where the logic becomes clear:

+	iommu_group = iommu_group_get(dev);
+#ifdef CONFIG_VFIO_NOIOMMU
+	if (!iommu_group && noiommu && !iommu_present(dev->bus)) {
+		/*
+		 * With noiommu enabled, create an IOMMU group for devices that
+		 * don't already have one and don't have an iommu_ops on their
+		 * bus.  Taint the kernel because we're about to give a DMA
+		 * capable device to a user without IOMMU protection.
+		 */
+		group = vfio_noiommu_group_alloc(dev);
+		if (group) {
+			add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+			dev_warn(dev, "Adding kernel taint for vfio-noiommu group on device\n");
+		}
+		return group;

Eg we never do a pointless vfio_group_get_from_iommu() on a no-iommu
group in the first place, we just create everything directly.

It would be fine to add an extra label and then remove it in patch 6,
but it is also fine this way and properly cleaned by the end.

Jason
Tian, Kevin Aug. 27, 2021, 2:07 a.m. UTC | #3
> From: Jason Gunthorpe <jgg@nvidia.com>
> Sent: Friday, August 27, 2021 7:36 AM
> 
> On Thu, Aug 26, 2021 at 01:54:13PM -0600, Alex Williamson wrote:
> > On Thu, 26 Aug 2021 15:34:14 +0200
> > Christoph Hellwig <hch@lst.de> wrote:
> >
> > > Factor out a helper to find or allocate the vfio_group to reduce the
> > > spagetthi code in vfio_register_group_dev a little.
> > >
> > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
> > >  drivers/vfio/vfio.c | 59 ++++++++++++++++++++++++++-------------------
> > >  1 file changed, 34 insertions(+), 25 deletions(-)
> > >
> > > diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
> > > index 18e4c7906d1b3f..852fe22125520d 100644
> > > +++ b/drivers/vfio/vfio.c
> > > @@ -823,10 +823,38 @@ void vfio_uninit_group_dev(struct vfio_device
> *device)
> > >  }
> > >  EXPORT_SYMBOL_GPL(vfio_uninit_group_dev);
> > >
> > > +struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
> > > +{
> > > +	struct iommu_group *iommu_group;
> > > +	struct vfio_group *group;
> > > +
> > > +	iommu_group = vfio_iommu_group_get(dev);
> > > +	if (!iommu_group)
> > > +		return ERR_PTR(-EINVAL);
> > > +
> > > +	/* a found vfio_group already holds a reference to the iommu_group
> */
> > > +	group = vfio_group_get_from_iommu(iommu_group);
> > > +	if (group)
> > > +		goto out_put;
> > > +
> > > +	/* a newly created vfio_group keeps the reference. */
> > > +	group = vfio_create_group(iommu_group);
> > > +	if (IS_ERR(group))
> > > +		goto out_put;
> > > +	return group;
> > > +
> > > +out_put:
> > > +#ifdef CONFIG_VFIO_NOIOMMU
> > > +	if (iommu_group_get_iommudata(iommu_group) == &noiommu)
> > > +		iommu_group_remove_device(dev);
> > > +#endif
> >
> > When we get here via the first goto above, it doesn't match the code
> > we're removing below.
> 
> If we are in noiommu mode then the group is a new singleton group and
> vfio_group_get_from_iommu() cannot succeed, so the out_put cannot
> trigger for the noiommu path.
> 
> This is all improved in patch 6 where the logic becomes clear:

patch 5. 
Alex Williamson Aug. 27, 2021, 2:35 p.m. UTC | #4
On Thu, 26 Aug 2021 20:35:58 -0300
Jason Gunthorpe <jgg@nvidia.com> wrote:

> On Thu, Aug 26, 2021 at 01:54:13PM -0600, Alex Williamson wrote:
> > On Thu, 26 Aug 2021 15:34:14 +0200
> > Christoph Hellwig <hch@lst.de> wrote:
> >   
> > > Factor out a helper to find or allocate the vfio_group to reduce the
> > > spagetthi code in vfio_register_group_dev a little.
> > > 
> > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
> > >  drivers/vfio/vfio.c | 59 ++++++++++++++++++++++++++-------------------
> > >  1 file changed, 34 insertions(+), 25 deletions(-)
> > > 
> > > diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
> > > index 18e4c7906d1b3f..852fe22125520d 100644
> > > +++ b/drivers/vfio/vfio.c
> > > @@ -823,10 +823,38 @@ void vfio_uninit_group_dev(struct vfio_device *device)
> > >  }
> > >  EXPORT_SYMBOL_GPL(vfio_uninit_group_dev);
> > >  
> > > +struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
> > > +{
> > > +	struct iommu_group *iommu_group;
> > > +	struct vfio_group *group;
> > > +
> > > +	iommu_group = vfio_iommu_group_get(dev);
> > > +	if (!iommu_group)
> > > +		return ERR_PTR(-EINVAL);
> > > +
> > > +	/* a found vfio_group already holds a reference to the iommu_group */
> > > +	group = vfio_group_get_from_iommu(iommu_group);
> > > +	if (group)
> > > +		goto out_put;
> > > +
> > > +	/* a newly created vfio_group keeps the reference. */
> > > +	group = vfio_create_group(iommu_group);
> > > +	if (IS_ERR(group))
> > > +		goto out_put;
> > > +	return group;
> > > +
> > > +out_put:
> > > +#ifdef CONFIG_VFIO_NOIOMMU
> > > +	if (iommu_group_get_iommudata(iommu_group) == &noiommu)
> > > +		iommu_group_remove_device(dev);
> > > +#endif  
> > 
> > When we get here via the first goto above, it doesn't match the code
> > we're removing below.   
> 
> If we are in noiommu mode then the group is a new singleton group and
> vfio_group_get_from_iommu() cannot succeed, so the out_put cannot
> trigger for the noiommu path.
> 
> This is all improved in patch 6 where the logic becomes clear:
> 
> +	iommu_group = iommu_group_get(dev);
> +#ifdef CONFIG_VFIO_NOIOMMU
> +	if (!iommu_group && noiommu && !iommu_present(dev->bus)) {
> +		/*
> +		 * With noiommu enabled, create an IOMMU group for devices that
> +		 * don't already have one and don't have an iommu_ops on their
> +		 * bus.  Taint the kernel because we're about to give a DMA
> +		 * capable device to a user without IOMMU protection.
> +		 */
> +		group = vfio_noiommu_group_alloc(dev);
> +		if (group) {
> +			add_taint(TAINT_USER, LOCKDEP_STILL_OK);
> +			dev_warn(dev, "Adding kernel taint for vfio-noiommu group on device\n");
> +		}
> +		return group;
> 
> Eg we never do a pointless vfio_group_get_from_iommu() on a no-iommu
> group in the first place, we just create everything directly.
> 
> It would be fine to add an extra label and then remove it in patch 6,
> but it is also fine this way and properly cleaned by the end.

I agree that it's resolved later in the series, but it's confusing
here, particularly because in patch 1 we need to come to the conclusion
that path is unreachable, thus the different return paths, then we
ignore it here with a common exit.  Thanks,

Alex
diff mbox series

Patch

diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 18e4c7906d1b3f..852fe22125520d 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -823,10 +823,38 @@  void vfio_uninit_group_dev(struct vfio_device *device)
 }
 EXPORT_SYMBOL_GPL(vfio_uninit_group_dev);
 
+struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
+{
+	struct iommu_group *iommu_group;
+	struct vfio_group *group;
+
+	iommu_group = vfio_iommu_group_get(dev);
+	if (!iommu_group)
+		return ERR_PTR(-EINVAL);
+
+	/* a found vfio_group already holds a reference to the iommu_group */
+	group = vfio_group_get_from_iommu(iommu_group);
+	if (group)
+		goto out_put;
+
+	/* a newly created vfio_group keeps the reference. */
+	group = vfio_create_group(iommu_group);
+	if (IS_ERR(group))
+		goto out_put;
+	return group;
+
+out_put:
+#ifdef CONFIG_VFIO_NOIOMMU
+	if (iommu_group_get_iommudata(iommu_group) == &noiommu)
+		iommu_group_remove_device(dev);
+#endif
+	iommu_group_put(iommu_group);
+	return group;
+}
+
 int vfio_register_group_dev(struct vfio_device *device)
 {
 	struct vfio_device *existing_device;
-	struct iommu_group *iommu_group;
 	struct vfio_group *group;
 
 	/*
@@ -836,36 +864,17 @@  int vfio_register_group_dev(struct vfio_device *device)
 	if (!device->dev_set)
 		vfio_assign_device_set(device, device);
 
-	iommu_group = vfio_iommu_group_get(device->dev);
-	if (!iommu_group)
-		return -EINVAL;
-
-	group = vfio_group_get_from_iommu(iommu_group);
-	if (!group) {
-		group = vfio_create_group(iommu_group);
-		if (IS_ERR(group)) {
-#ifdef CONFIG_VFIO_NOIOMMU
-			if (iommu_group_get_iommudata(iommu_group) == &noiommu)
-				iommu_group_remove_device(device->dev);
-#endif
-			iommu_group_put(iommu_group);
-			return PTR_ERR(group);
-		}
-	} else {
-		/*
-		 * A found vfio_group already holds a reference to the
-		 * iommu_group.  A created vfio_group keeps the reference.
-		 */
-		iommu_group_put(iommu_group);
-	}
+	group = vfio_group_find_or_alloc(device->dev);
+	if (IS_ERR(group))
+		return PTR_ERR(group);
 
 	existing_device = vfio_group_get_device(group, device->dev);
 	if (existing_device) {
 		dev_WARN(device->dev, "Device already exists on group %d\n",
-			 iommu_group_id(iommu_group));
+			 iommu_group_id(group->iommu_group));
 		vfio_device_put(existing_device);
 #ifdef CONFIG_VFIO_NOIOMMU
-		if (iommu_group_get_iommudata(iommu_group) == &noiommu)
+		if (iommu_group_get_iommudata(group->iommu_group) == &noiommu)
 			iommu_group_remove_device(device->dev);
 #endif
 		vfio_group_put(group);