From b665bd98fe63165aa1d8a26d2dc50d074c87c111 Mon Sep 17 00:00:00 2001
From: Tarun Gupta <targupta@nvidia.com>
Date: Wed, 12 Jul 2023 09:45:09 +0200
Subject: Restore kABI for NVidia vGPU driver
References: bsc#1210825
Patch-mainline: Never, (re-)adding support for out-of-tree module
This is a partial revert of these upstream patches:
fda49d97f2c4 ("vfio: remove the unused mdev iommu hook")
c3c0fa9d94f7 ("vfio: clean up the check for mediated device in vfio_iommu_type1")
2815fe149ffa ("vfio/mdev: unexport mdev_bus_type")
to restore functionality required for the proprietary NVidia vGPU
driver to work on SLE15-SP5.
The revert is based on a patch written by Tarun Gupta from NVidia.
Signed-off-by: Joerg Roedel <jroedel@suse.de>
---
drivers/vfio/vfio_iommu_type1.c | 126 +++++++++++++++++++++++++++++---
include/linux/mdev.h | 20 +++++
2 files changed, 137 insertions(+), 9 deletions(-)
diff --git a/drivers/vfio/mdev/mdev_driver.c b/drivers/vfio/mdev/mdev_driver.c
--- a/drivers/vfio/mdev/mdev_driver.c
+++ b/drivers/vfio/mdev/mdev_driver.c
@@ -46,6 +46,7 @@
.remove = mdev_remove,
.match = mdev_match,
};
+EXPORT_SYMBOL(mdev_bus_type);
/**
* mdev_register_driver - register a new MDEV driver
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index eb56cb79f8f9..8337b2304612 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -36,6 +36,7 @@
#include <linux/uaccess.h>
#include <linux/vfio.h>
#include <linux/workqueue.h>
+#include <linux/mdev.h>
#include <linux/notifier.h>
#include "vfio.h"
@@ -116,6 +117,7 @@ struct vfio_batch {
struct vfio_iommu_group {
struct iommu_group *iommu_group;
struct list_head next;
+ bool mdev_group;
bool pinned_page_dirty_scope;
};
@@ -1733,6 +1735,18 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
return ret;
}
+static int vfio_bus_type(struct device *dev, void *data)
+{
+ const 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)
{
@@ -1972,6 +1986,81 @@ static bool vfio_iommu_has_sw_msi(struct list_head *group_resv_regions,
return ret;
}
+static int vfio_mdev_attach_domain(struct device *dev, void *data)
+{
+ struct mdev_device *mdev = to_mdev_device(dev);
+ struct iommu_domain *domain = data;
+ struct device *iommu_device;
+
+ iommu_device = mdev_get_iommu_device(mdev);
+ if (iommu_device)
+ return iommu_attach_device(domain, iommu_device);
+
+ return -EINVAL;
+}
+
+static int vfio_mdev_detach_domain(struct device *dev, void *data)
+{
+ struct mdev_device *mdev = to_mdev_device(dev);
+ struct iommu_domain *domain = data;
+ struct device *iommu_device;
+
+ iommu_device = mdev_get_iommu_device(mdev);
+ if (iommu_device)
+ iommu_detach_device(domain, iommu_device);
+
+ return 0;
+}
+
+static int vfio_iommu_attach_group(struct vfio_domain *domain,
+ struct vfio_iommu_group *group)
+{
+ if (group->mdev_group)
+ return iommu_group_for_each_dev(group->iommu_group,
+ domain->domain,
+ vfio_mdev_attach_domain);
+ else
+ return iommu_attach_group(domain->domain, group->iommu_group);
+}
+
+static void vfio_iommu_detach_group(struct vfio_domain *domain,
+ struct vfio_iommu_group *group)
+{
+ if (group->mdev_group)
+ iommu_group_for_each_dev(group->iommu_group, domain->domain,
+ vfio_mdev_detach_domain);
+ else
+ iommu_detach_group(domain->domain, group->iommu_group);
+}
+
+static bool vfio_bus_is_mdev(struct bus_type *bus)
+{
+ struct bus_type *mdev_bus;
+ bool ret = false;
+
+ mdev_bus = symbol_get(mdev_bus_type);
+ if (mdev_bus) {
+ ret = (bus == mdev_bus);
+ symbol_put(mdev_bus_type);
+ }
+
+ return ret;
+}
+
+static int vfio_mdev_iommu_device(struct device *dev, void *data)
+{
+ struct mdev_device *mdev = to_mdev_device(dev);
+ struct device **old = data, *new;
+
+ new = mdev_get_iommu_device(mdev);
+ if (!new || (*old && *old != new))
+ return -EINVAL;
+
+ *old = new;
+
+ return 0;
+}
+
/*
* This is a helper function to insert an address range to iova list.
* The list is initially created with a single entry corresponding to
@@ -2235,6 +2324,25 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
group->iommu_group = iommu_group;
if (type == VFIO_EMULATED_IOMMU) {
+ struct bus_type *bus = NULL;
+
+ ret = iommu_group_for_each_dev(iommu_group, &bus, vfio_bus_type);
+
+ if (!ret && vfio_bus_is_mdev(bus)) {
+ struct device *iommu_device = NULL;
+
+ group->mdev_group = true;
+
+ /* Determine the isolation type */
+ ret = iommu_group_for_each_dev(iommu_group,
+ &iommu_device,
+ vfio_mdev_iommu_device);
+ if (!ret && iommu_device) {
+ iommu_group = iommu_device->iommu_group;
+ goto mdev_iommu_device;
+ }
+ }
+
list_add(&group->next, &iommu->emulated_iommu_groups);
/*
* An emulated IOMMU group cannot dirty memory directly, it can
@@ -2247,6 +2355,8 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
goto out_unlock;
}
+mdev_iommu_device:
+
ret = -ENOMEM;
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
if (!domain)
@@ -2269,7 +2379,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
goto out_domain;
}
- ret = iommu_attach_group(domain->domain, group->iommu_group);
+ ret = vfio_iommu_attach_group(domain, group);
if (ret)
goto out_domain;
@@ -2345,17 +2455,15 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
if (d->domain->ops == domain->domain->ops &&
d->enforce_cache_coherency ==
domain->enforce_cache_coherency) {
- iommu_detach_group(domain->domain, group->iommu_group);
- if (!iommu_attach_group(d->domain,
- group->iommu_group)) {
+ vfio_iommu_detach_group(domain, group);
+ if (!vfio_iommu_attach_group(d, group)) {
list_add(&group->next, &d->group_list);
iommu_domain_free(domain->domain);
kfree(domain);
goto done;
}
- ret = iommu_attach_group(domain->domain,
- group->iommu_group);
+ ret = vfio_iommu_attach_group(domain, group);
if (ret)
goto out_domain;
}
@@ -2392,7 +2500,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
return 0;
out_detach:
- iommu_detach_group(domain->domain, group->iommu_group);
+ vfio_iommu_detach_group(domain, group);
out_domain:
iommu_domain_free(domain->domain);
vfio_iommu_iova_free(&iova_copy);
@@ -2553,7 +2661,7 @@ static void vfio_iommu_type1_detach_group(void *iommu_data,
if (!group)
continue;
- iommu_detach_group(domain->domain, group->iommu_group);
+ vfio_iommu_detach_group(domain, group);
update_dirty_scope = !group->pinned_page_dirty_scope;
list_del(&group->next);
kfree(group);
@@ -2644,7 +2752,7 @@ static void vfio_release_domain(struct vfio_domain *domain)
list_for_each_entry_safe(group, group_tmp,
&domain->group_list, next) {
- iommu_detach_group(domain->domain, group->iommu_group);
+ vfio_iommu_detach_group(domain, group);
list_del(&group->next);
kfree(group);
}
diff --git a/include/linux/mdev.h b/include/linux/mdev.h
index 47ad3b104d9e..02bee428e37f 100644
--- a/include/linux/mdev.h
+++ b/include/linux/mdev.h
@@ -17,6 +17,7 @@ struct mdev_device {
guid_t uuid;
struct list_head next;
struct mdev_type *type;
+ struct device *iommu_device;
bool active;
};
@@ -49,6 +50,25 @@ struct mdev_type_attribute mdev_type_attr_##_name = \
return container_of(dev, struct mdev_device, dev);
}
+/*
+ * Called by the parent device driver to set the device which represents
+ * this mdev in iommu protection scope. By default, the iommu device is
+ * NULL, that indicates using vendor defined isolation.
+ *
+ * @dev: the mediated device that iommu will isolate.
+ * @iommu_device: a pci device which represents the iommu for @dev.
+ */
+static inline void mdev_set_iommu_device(struct mdev_device *mdev,
+ struct device *iommu_device)
+{
+ mdev->iommu_device = iommu_device;
+}
+
+static inline struct device *mdev_get_iommu_device(struct mdev_device *mdev)
+{
+ return mdev->iommu_device;
+}
+
/**
* struct mdev_driver - Mediated device driver
* @device_api: string to return for the device_api sysfs
@@ -85,5 +105,7 @@
{
return &mdev->dev;
}
+
+extern struct bus_type mdev_bus_type;
#endif /* MDEV_H */
--
2.41.0