linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] vfio/type1: Simplify bus_type determination
@ 2022-06-08 14:25 Robin Murphy
  2022-06-08 14:25 ` [PATCH 2/2] vfio: Use device_iommu_capable() Robin Murphy
  2022-06-10  0:03 ` [PATCH 1/2] vfio/type1: Simplify bus_type determination Jason Gunthorpe
  0 siblings, 2 replies; 5+ messages in thread
From: Robin Murphy @ 2022-06-08 14:25 UTC (permalink / raw)
  To: alex.williamson, cohuck; +Cc: kvm, iommu, linux-kernel, jgg, baolu.lu

Since IOMMU groups are mandatory for drivers to support, it stands to
reason that any device which has been successfully be added to a group
must be on a bus supported by that IOMMU driver, and therefore a domain
viable for any device in the group must be viable for all devices in
the group. This already has to be the case for the IOMMU API's internal
default domain, for instance. Thus even if the group contains devices
on different buses, that can only mean that the IOMMU driver actually
supports such an odd topology, and so without loss of generality we can
expect the bus type of any arbitrary device in a group to be suitable
for IOMMU API calls.

Replace vfio_bus_type() with a trivial callback that simply returns any
device from which to then derive a usable bus type. This is also a step
towards removing the vague bus-based interfaces from the IOMMU API.

Furthermore, scrutiny reveals a lack of protection for the bus and/or
device being removed while .attach_group is inspecting them; the
reference we hold on the iommu_group ensures that data remains valid,
but does not prevent the group's membership changing underfoot. Holding
the vfio_goup's device_lock should be sufficient to block any relevant
device's VFIO driver from unregistering, and thus block unbinding and
any further stages of removal for the duration of the attach operation.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---

With vfio_group_viable() now gone and no longer taking the vfio_group
device_lock inside the iommu_group mutex, this seems workable, and at
least lockdep is happy.

 drivers/vfio/vfio.c             |  6 ++++++
 drivers/vfio/vfio_iommu_type1.c | 32 ++++++++++++++------------------
 2 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 61e71c1154be..0b71f3d12e5f 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -760,8 +760,11 @@ static int __vfio_container_attach_groups(struct vfio_container *container,
 	int ret = -ENODEV;
 
 	list_for_each_entry(group, &container->group_list, container_next) {
+		/* Prevent devices unregistering during attach */
+		mutex_lock(&group->device_lock);
 		ret = driver->ops->attach_group(data, group->iommu_group,
 						group->type);
+		mutex_unlock(&group->device_lock);
 		if (ret)
 			goto unwind;
 	}
@@ -1017,9 +1020,12 @@ static int vfio_group_set_container(struct vfio_group *group, int container_fd)
 
 	driver = container->iommu_driver;
 	if (driver) {
+		/* Prevent devices unregistering during attach */
+		mutex_lock(&group->device_lock);
 		ret = driver->ops->attach_group(container->iommu_data,
 						group->iommu_group,
 						group->type);
+		mutex_unlock(&group->device_lock);
 		if (ret) {
 			if (group->type == VFIO_IOMMU)
 				iommu_group_release_dma_owner(
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index c13b9290e357..5c19faef90ae 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -1679,18 +1679,6 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
 	return ret;
 }
 
-static int vfio_bus_type(struct device *dev, void *data)
-{
-	struct bus_type **bus = data;
-
-	if (*bus && *bus != dev->bus)
-		return -EINVAL;
-
-	*bus = dev->bus;
-
-	return 0;
-}
-
 static int vfio_iommu_replay(struct vfio_iommu *iommu,
 			     struct vfio_domain *domain)
 {
@@ -2153,13 +2141,20 @@ static void vfio_iommu_iova_insert_copy(struct vfio_iommu *iommu,
 	list_splice_tail(iova_copy, iova);
 }
 
+static int vfio_first_dev(struct device *dev, void *data)
+{
+	/* Just grab the first device and return nonzero to stop iterating */
+	*(struct device **)data = dev;
+	return 1;
+}
+
 static int vfio_iommu_type1_attach_group(void *iommu_data,
 		struct iommu_group *iommu_group, enum vfio_group_type type)
 {
 	struct vfio_iommu *iommu = iommu_data;
 	struct vfio_iommu_group *group;
 	struct vfio_domain *domain, *d;
-	struct bus_type *bus = NULL;
+	struct device *iommu_api_dev = NULL;
 	bool resv_msi, msi_remap;
 	phys_addr_t resv_msi_base = 0;
 	struct iommu_domain_geometry *geo;
@@ -2192,9 +2187,10 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
 		goto out_unlock;
 	}
 
-	/* Determine bus_type in order to allocate a domain */
-	ret = iommu_group_for_each_dev(iommu_group, &bus, vfio_bus_type);
-	if (ret)
+	/* Resolve the group back to a device for IOMMU API ops */
+	ret = -ENODEV;
+	iommu_group_for_each_dev(iommu_group, &iommu_api_dev, vfio_first_dev);
+	if (!iommu_api_dev)
 		goto out_free_group;
 
 	ret = -ENOMEM;
@@ -2203,7 +2199,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
 		goto out_free_group;
 
 	ret = -EIO;
-	domain->domain = iommu_domain_alloc(bus);
+	domain->domain = iommu_domain_alloc(iommu_api_dev->bus);
 	if (!domain->domain)
 		goto out_free_domain;
 
@@ -2258,7 +2254,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
 	list_add(&group->next, &domain->group_list);
 
 	msi_remap = irq_domain_check_msi_remap() ||
-		    iommu_capable(bus, IOMMU_CAP_INTR_REMAP);
+		    iommu_capable(iommu_api_dev->bus, IOMMU_CAP_INTR_REMAP);
 
 	if (!allow_unsafe_interrupts && !msi_remap) {
 		pr_warn("%s: No interrupt remapping support.  Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
-- 
2.36.1.dirty


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] vfio: Use device_iommu_capable()
  2022-06-08 14:25 [PATCH 1/2] vfio/type1: Simplify bus_type determination Robin Murphy
@ 2022-06-08 14:25 ` Robin Murphy
  2022-06-10  0:03 ` [PATCH 1/2] vfio/type1: Simplify bus_type determination Jason Gunthorpe
  1 sibling, 0 replies; 5+ messages in thread
From: Robin Murphy @ 2022-06-08 14:25 UTC (permalink / raw)
  To: alex.williamson, cohuck; +Cc: kvm, iommu, linux-kernel, jgg, baolu.lu

Use the new interface to check the capabilities for our device
specifically.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/vfio/vfio.c             | 2 +-
 drivers/vfio/vfio_iommu_type1.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 0b71f3d12e5f..ac2a8d18e2e4 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -605,7 +605,7 @@ int vfio_register_group_dev(struct vfio_device *device)
 	 * VFIO always sets IOMMU_CACHE because we offer no way for userspace to
 	 * restore cache coherency.
 	 */
-	if (!iommu_capable(device->dev->bus, IOMMU_CAP_CACHE_COHERENCY))
+	if (!device_iommu_capable(device->dev, IOMMU_CAP_CACHE_COHERENCY))
 		return -EINVAL;
 
 	return __vfio_register_dev(device,
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 5c19faef90ae..8ddce0e08169 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -2254,7 +2254,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
 	list_add(&group->next, &domain->group_list);
 
 	msi_remap = irq_domain_check_msi_remap() ||
-		    iommu_capable(iommu_api_dev->bus, IOMMU_CAP_INTR_REMAP);
+		    device_iommu_capable(iommu_api_dev, IOMMU_CAP_INTR_REMAP);
 
 	if (!allow_unsafe_interrupts && !msi_remap) {
 		pr_warn("%s: No interrupt remapping support.  Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
-- 
2.36.1.dirty


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] vfio/type1: Simplify bus_type determination
  2022-06-08 14:25 [PATCH 1/2] vfio/type1: Simplify bus_type determination Robin Murphy
  2022-06-08 14:25 ` [PATCH 2/2] vfio: Use device_iommu_capable() Robin Murphy
@ 2022-06-10  0:03 ` Jason Gunthorpe
  2022-06-21 19:09   ` Robin Murphy
  1 sibling, 1 reply; 5+ messages in thread
From: Jason Gunthorpe @ 2022-06-10  0:03 UTC (permalink / raw)
  To: Robin Murphy; +Cc: alex.williamson, cohuck, kvm, iommu, linux-kernel, baolu.lu

On Wed, Jun 08, 2022 at 03:25:49PM +0100, Robin Murphy wrote:
> Since IOMMU groups are mandatory for drivers to support, it stands to
> reason that any device which has been successfully be added to a group
> must be on a bus supported by that IOMMU driver, and therefore a domain
> viable for any device in the group must be viable for all devices in
> the group. This already has to be the case for the IOMMU API's internal
> default domain, for instance. Thus even if the group contains devices
> on different buses, that can only mean that the IOMMU driver actually
> supports such an odd topology, and so without loss of generality we can
> expect the bus type of any arbitrary device in a group to be suitable
> for IOMMU API calls.
> 
> Replace vfio_bus_type() with a trivial callback that simply returns any
> device from which to then derive a usable bus type. This is also a step
> towards removing the vague bus-based interfaces from the IOMMU API.
> 
> Furthermore, scrutiny reveals a lack of protection for the bus and/or
> device being removed while .attach_group is inspecting them; the
> reference we hold on the iommu_group ensures that data remains valid,
> but does not prevent the group's membership changing underfoot. Holding
> the vfio_goup's device_lock should be sufficient to block any relevant
> device's VFIO driver from unregistering, and thus block unbinding and
> any further stages of removal for the duration of the attach operation.

The device_lock only protects devices that are on the device_list from
concurrent unregistration, the device returned by
iommu_group_for_each_dev() is not guarented to be the on the device
list.

> @@ -760,8 +760,11 @@ static int __vfio_container_attach_groups(struct vfio_container *container,
>  	int ret = -ENODEV;
>  
>  	list_for_each_entry(group, &container->group_list, container_next) {
> +		/* Prevent devices unregistering during attach */
> +		mutex_lock(&group->device_lock);
>  		ret = driver->ops->attach_group(data, group->iommu_group,
>  						group->type);
> +		mutex_unlock(&group->device_lock);

I still prefer the version where we pass in an arbitrary vfio_device
from the list the group maintains:

   list_first_entry(group->device_list)

And don't call iommu_group_for_each_dev(), it is much simpler to
reason about how it works.

Jason

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] vfio/type1: Simplify bus_type determination
  2022-06-10  0:03 ` [PATCH 1/2] vfio/type1: Simplify bus_type determination Jason Gunthorpe
@ 2022-06-21 19:09   ` Robin Murphy
  2022-06-24 14:14     ` Jason Gunthorpe
  0 siblings, 1 reply; 5+ messages in thread
From: Robin Murphy @ 2022-06-21 19:09 UTC (permalink / raw)
  To: Jason Gunthorpe; +Cc: kvm, cohuck, iommu, linux-kernel, alex.williamson

On 2022-06-10 01:03, Jason Gunthorpe via iommu wrote:
> On Wed, Jun 08, 2022 at 03:25:49PM +0100, Robin Murphy wrote:
>> Since IOMMU groups are mandatory for drivers to support, it stands to
>> reason that any device which has been successfully be added to a group
>> must be on a bus supported by that IOMMU driver, and therefore a domain
>> viable for any device in the group must be viable for all devices in
>> the group. This already has to be the case for the IOMMU API's internal
>> default domain, for instance. Thus even if the group contains devices
>> on different buses, that can only mean that the IOMMU driver actually
>> supports such an odd topology, and so without loss of generality we can
>> expect the bus type of any arbitrary device in a group to be suitable
>> for IOMMU API calls.
>>
>> Replace vfio_bus_type() with a trivial callback that simply returns any
>> device from which to then derive a usable bus type. This is also a step
>> towards removing the vague bus-based interfaces from the IOMMU API.
>>
>> Furthermore, scrutiny reveals a lack of protection for the bus and/or
>> device being removed while .attach_group is inspecting them; the
>> reference we hold on the iommu_group ensures that data remains valid,
>> but does not prevent the group's membership changing underfoot. Holding
>> the vfio_goup's device_lock should be sufficient to block any relevant
>> device's VFIO driver from unregistering, and thus block unbinding and
>> any further stages of removal for the duration of the attach operation.
> 
> The device_lock only protects devices that are on the device_list from
> concurrent unregistration, the device returned by
> iommu_group_for_each_dev() is not guarented to be the on the device
> list.

Sigh, you're quite right, and now I have a vague feeling that you called 
that out in the previous discussion too, so apologies for forgetting.

>> @@ -760,8 +760,11 @@ static int __vfio_container_attach_groups(struct vfio_container *container,
>>   	int ret = -ENODEV;
>>   
>>   	list_for_each_entry(group, &container->group_list, container_next) {
>> +		/* Prevent devices unregistering during attach */
>> +		mutex_lock(&group->device_lock);
>>   		ret = driver->ops->attach_group(data, group->iommu_group,
>>   						group->type);
>> +		mutex_unlock(&group->device_lock);
> 
> I still prefer the version where we pass in an arbitrary vfio_device
> from the list the group maintains:
> 
>     list_first_entry(group->device_list)
> 
> And don't call iommu_group_for_each_dev(), it is much simpler to
> reason about how it works.

Agreed, trying to figure out which are the VFIO devices from within the 
iommu_group iterator seems beyond the threshold of practicality.

Quick consensus then: does anyone have a particular preference between 
changing the .attach_group signature vs. adding a helper based on 
vfio_group_get_from_iommu() for type1 to call from within its callback? 
They seem about equal (but opposite) in terms of the simplicity vs. 
impact tradeoff to me, so I can't quite decide conclusively...

Thanks,
Robin.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] vfio/type1: Simplify bus_type determination
  2022-06-21 19:09   ` Robin Murphy
@ 2022-06-24 14:14     ` Jason Gunthorpe
  0 siblings, 0 replies; 5+ messages in thread
From: Jason Gunthorpe @ 2022-06-24 14:14 UTC (permalink / raw)
  To: Robin Murphy; +Cc: kvm, cohuck, iommu, linux-kernel, alex.williamson

On Tue, Jun 21, 2022 at 08:09:20PM +0100, Robin Murphy wrote:

> Quick consensus then: does anyone have a particular preference between
> changing the .attach_group signature vs. adding a helper based on
> vfio_group_get_from_iommu() for type1 to call from within its callback? They
> seem about equal (but opposite) in terms of the simplicity vs. impact
> tradeoff to me, so I can't quite decide conclusively...

Ah, I've been away and only just saw this..

Given Alex's remarks and the need to re-get the vfio_group in the
helper, it may be very slightly nicer to pass in the vfio_device as an
argument. But either works for me.

Jason

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-06-24 14:14 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-08 14:25 [PATCH 1/2] vfio/type1: Simplify bus_type determination Robin Murphy
2022-06-08 14:25 ` [PATCH 2/2] vfio: Use device_iommu_capable() Robin Murphy
2022-06-10  0:03 ` [PATCH 1/2] vfio/type1: Simplify bus_type determination Jason Gunthorpe
2022-06-21 19:09   ` Robin Murphy
2022-06-24 14:14     ` Jason Gunthorpe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).