diff options
Diffstat (limited to 'drivers/vfio')
-rw-r--r-- | drivers/vfio/Kconfig | 2 | ||||
-rw-r--r-- | drivers/vfio/mdev/Kconfig | 1 | ||||
-rw-r--r-- | drivers/vfio/mdev/Makefile | 1 | ||||
-rw-r--r-- | drivers/vfio/mdev/mdev_core.c | 36 | ||||
-rw-r--r-- | drivers/vfio/mdev/mdev_private.h | 2 | ||||
-rw-r--r-- | drivers/vfio/mdev/mdev_sysfs.c | 2 | ||||
-rw-r--r-- | drivers/vfio/pci/Kconfig | 1 | ||||
-rw-r--r-- | drivers/vfio/pci/Makefile | 1 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 27 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_config.c | 29 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_nvlink2.c | 2 | ||||
-rw-r--r-- | drivers/vfio/platform/Kconfig | 1 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/Kconfig | 1 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/vfio_platform_amdxgbe.c | 5 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_platform_common.c | 12 | ||||
-rw-r--r-- | drivers/vfio/vfio.c | 59 | ||||
-rw-r--r-- | drivers/vfio/vfio_iommu_spapr_tce.c | 5 | ||||
-rw-r--r-- | drivers/vfio/vfio_iommu_type1.c | 156 |
18 files changed, 233 insertions, 110 deletions
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 9de5ed38da83..e5a7a454fe17 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config VFIO_IOMMU_TYPE1 tristate depends on VFIO @@ -22,7 +23,6 @@ menuconfig VFIO tristate "VFIO Non-Privileged userspace driver framework" depends on IOMMU_API select VFIO_IOMMU_TYPE1 if (X86 || S390 || ARM || ARM64) - select ANON_INODES help VFIO provides a framework for secure userspace device drivers. See Documentation/vfio.txt for more details. diff --git a/drivers/vfio/mdev/Kconfig b/drivers/vfio/mdev/Kconfig index 14fdb106a827..ba94a076887f 100644 --- a/drivers/vfio/mdev/Kconfig +++ b/drivers/vfio/mdev/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config VFIO_MDEV tristate "Mediated device driver framework" diff --git a/drivers/vfio/mdev/Makefile b/drivers/vfio/mdev/Makefile index fa2d5ea466ee..101516fdf375 100644 --- a/drivers/vfio/mdev/Makefile +++ b/drivers/vfio/mdev/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only mdev-y := mdev_core.o mdev_sysfs.o mdev_driver.o diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c index b96fedc77ee5..3cc1a05fde1c 100644 --- a/drivers/vfio/mdev/mdev_core.c +++ b/drivers/vfio/mdev/mdev_core.c @@ -88,7 +88,7 @@ static void mdev_release_parent(struct kref *kref) put_device(dev); } -static inline struct mdev_parent *mdev_get_parent(struct mdev_parent *parent) +static struct mdev_parent *mdev_get_parent(struct mdev_parent *parent) { if (parent) kref_get(&parent->ref); @@ -96,7 +96,7 @@ static inline struct mdev_parent *mdev_get_parent(struct mdev_parent *parent) return parent; } -static inline void mdev_put_parent(struct mdev_parent *parent) +static void mdev_put_parent(struct mdev_parent *parent) { if (parent) kref_put(&parent->ref, mdev_release_parent); @@ -141,7 +141,7 @@ static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove) */ ret = parent->ops->remove(mdev); if (ret && !force_remove) - return -EBUSY; + return ret; sysfs_remove_groups(&mdev->dev.kobj, parent->ops->mdev_attr_groups); return 0; @@ -149,10 +149,10 @@ static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove) static int mdev_device_remove_cb(struct device *dev, void *data) { - if (!dev_is_mdev(dev)) - return 0; + if (dev_is_mdev(dev)) + mdev_device_remove(dev, true); - return mdev_device_remove(dev, data ? *(bool *)data : true); + return 0; } /* @@ -181,6 +181,7 @@ int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops) /* Check for duplicate */ parent = __find_parent_device(dev); if (parent) { + parent = NULL; ret = -EEXIST; goto add_dev_err; } @@ -239,7 +240,6 @@ EXPORT_SYMBOL(mdev_register_device); void mdev_unregister_device(struct device *dev) { struct mdev_parent *parent; - bool force_remove = true; mutex_lock(&parent_list_lock); parent = __find_parent_device(dev); @@ -253,8 +253,7 @@ void mdev_unregister_device(struct device *dev) list_del(&parent->next); class_compat_remove_link(mdev_bus_compat_class, dev, NULL); - device_for_each_child(dev, (void *)&force_remove, - mdev_device_remove_cb); + device_for_each_child(dev, NULL, mdev_device_remove_cb); parent_remove_sysfs_files(parent); @@ -310,7 +309,6 @@ int mdev_device_create(struct kobject *kobj, mutex_unlock(&mdev_list_lock); mdev->parent = parent; - kref_init(&mdev->ref); mdev->dev.parent = dev; mdev->dev.bus = &mdev_bus_type; @@ -390,6 +388,24 @@ int mdev_device_remove(struct device *dev, bool force_remove) return 0; } +int mdev_set_iommu_device(struct device *dev, struct device *iommu_device) +{ + struct mdev_device *mdev = to_mdev_device(dev); + + mdev->iommu_device = iommu_device; + + return 0; +} +EXPORT_SYMBOL(mdev_set_iommu_device); + +struct device *mdev_get_iommu_device(struct device *dev) +{ + struct mdev_device *mdev = to_mdev_device(dev); + + return mdev->iommu_device; +} +EXPORT_SYMBOL(mdev_get_iommu_device); + static int __init mdev_init(void) { return mdev_bus_register(); diff --git a/drivers/vfio/mdev/mdev_private.h b/drivers/vfio/mdev/mdev_private.h index 379758c52b1b..36cbbdb754de 100644 --- a/drivers/vfio/mdev/mdev_private.h +++ b/drivers/vfio/mdev/mdev_private.h @@ -30,9 +30,9 @@ struct mdev_device { struct mdev_parent *parent; guid_t uuid; void *driver_data; - struct kref ref; struct list_head next; struct kobject *type_kobj; + struct device *iommu_device; bool active; }; diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c index 5193a0e0ce5a..cbf94b8165ea 100644 --- a/drivers/vfio/mdev/mdev_sysfs.c +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -280,7 +280,7 @@ type_link_failed: void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type) { + sysfs_remove_files(&dev->kobj, mdev_device_attrs); sysfs_remove_link(&dev->kobj, "mdev_type"); sysfs_remove_link(type->devices_kobj, dev_name(dev)); - sysfs_remove_files(&dev->kobj, mdev_device_attrs); } diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index d0f8e4f5a039..ac3c1dd3edef 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config VFIO_PCI tristate "VFIO support for PCI devices" depends on VFIO && PCI && EVENTFD diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile index 9662c063a6b1..f027f8a0e89c 100644 --- a/drivers/vfio/pci/Makefile +++ b/drivers/vfio/pci/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o vfio-pci-$(CONFIG_VFIO_PCI_IGD) += vfio_pci_igd.o diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index a25659b5a5d1..cab71da46f4a 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -12,6 +12,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define dev_fmt pr_fmt #include <linux/device.h> #include <linux/eventfd.h> @@ -287,12 +288,11 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) pci_save_state(pdev); vdev->pci_saved_state = pci_store_saved_state(pdev); if (!vdev->pci_saved_state) - pr_debug("%s: Couldn't store %s saved state\n", - __func__, dev_name(&pdev->dev)); + pci_dbg(pdev, "%s: Couldn't store saved state\n", __func__); if (likely(!nointxmask)) { if (vfio_pci_nointx(pdev)) { - dev_info(&pdev->dev, "Masking broken INTx support\n"); + pci_info(pdev, "Masking broken INTx support\n"); vdev->nointx = true; pci_intx(pdev, 0); } else @@ -336,8 +336,7 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) IS_ENABLED(CONFIG_VFIO_PCI_IGD)) { ret = vfio_pci_igd_init(vdev); if (ret) { - dev_warn(&vdev->pdev->dev, - "Failed to setup Intel IGD regions\n"); + pci_warn(pdev, "Failed to setup Intel IGD regions\n"); goto disable_exit; } } @@ -346,8 +345,7 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) IS_ENABLED(CONFIG_VFIO_PCI_NVLINK2)) { ret = vfio_pci_nvdia_v100_nvlink2_init(vdev); if (ret && ret != -ENODEV) { - dev_warn(&vdev->pdev->dev, - "Failed to setup NVIDIA NV2 RAM region\n"); + pci_warn(pdev, "Failed to setup NVIDIA NV2 RAM region\n"); goto disable_exit; } } @@ -356,8 +354,7 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) IS_ENABLED(CONFIG_VFIO_PCI_NVLINK2)) { ret = vfio_pci_ibm_npu2_init(vdev); if (ret && ret != -ENODEV) { - dev_warn(&vdev->pdev->dev, - "Failed to setup NVIDIA NV2 ATSD region\n"); + pci_warn(pdev, "Failed to setup NVIDIA NV2 ATSD region\n"); goto disable_exit; } } @@ -429,8 +426,7 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) * is just busy work. */ if (pci_load_and_free_saved_state(pdev, &vdev->pci_saved_state)) { - pr_info("%s: Couldn't reload %s saved state\n", - __func__, dev_name(&pdev->dev)); + pci_info(pdev, "%s: Couldn't reload saved state\n", __func__); if (!vdev->reset_works) goto out; @@ -1255,17 +1251,18 @@ static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) static void vfio_pci_request(void *device_data, unsigned int count) { struct vfio_pci_device *vdev = device_data; + struct pci_dev *pdev = vdev->pdev; mutex_lock(&vdev->igate); if (vdev->req_trigger) { if (!(count % 10)) - dev_notice_ratelimited(&vdev->pdev->dev, + pci_notice_ratelimited(pdev, "Relaying device request to user (#%u)\n", count); eventfd_signal(vdev->req_trigger, 1); } else if (count == 0) { - dev_warn(&vdev->pdev->dev, + pci_warn(pdev, "No device request channel registered, blocked until released by user\n"); } @@ -1661,11 +1658,11 @@ static void __init vfio_pci_fill_ids(void) rc = pci_add_dynid(&vfio_pci_driver, vendor, device, subvendor, subdevice, class, class_mask, 0); if (rc) - pr_warn("failed to add dynamic id [%04hx:%04hx[%04hx:%04hx]] class %#08x/%08x (%d)\n", + pr_warn("failed to add dynamic id [%04x:%04x[%04x:%04x]] class %#08x/%08x (%d)\n", vendor, device, subvendor, subdevice, class, class_mask, rc); else - pr_info("add [%04hx:%04hx[%04hx:%04hx]] class %#08x/%08x\n", + pr_info("add [%04x:%04x[%04x:%04x]] class %#08x/%08x\n", vendor, device, subvendor, subdevice, class, class_mask); } diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index e82b51114687..52963a904790 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -412,8 +412,7 @@ static void vfio_bar_restore(struct vfio_pci_device *vdev) if (pdev->is_virtfn) return; - pr_info("%s: %s reset recovery - restoring bars\n", - __func__, dev_name(&pdev->dev)); + pci_info(pdev, "%s: reset recovery - restoring BARs\n", __func__); for (i = PCI_BASE_ADDRESS_0; i <= PCI_BASE_ADDRESS_5; i += 4, rbar++) pci_user_write_config_dword(pdev, i, *rbar); @@ -1298,8 +1297,8 @@ static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos) else return PCI_SATA_SIZEOF_SHORT; default: - pr_warn("%s: %s unknown length for pci cap 0x%x@0x%x\n", - dev_name(&pdev->dev), __func__, cap, pos); + pci_warn(pdev, "%s: unknown length for PCI cap %#x@%#x\n", + __func__, cap, pos); } return 0; @@ -1372,8 +1371,8 @@ static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos) } return PCI_TPH_BASE_SIZEOF; default: - pr_warn("%s: %s unknown length for pci ecap 0x%x@0x%x\n", - dev_name(&pdev->dev), __func__, ecap, epos); + pci_warn(pdev, "%s: unknown length for PCI ecap %#x@%#x\n", + __func__, ecap, epos); } return 0; @@ -1474,8 +1473,8 @@ static int vfio_cap_init(struct vfio_pci_device *vdev) } if (!len) { - pr_info("%s: %s hiding cap 0x%x\n", - __func__, dev_name(&pdev->dev), cap); + pci_info(pdev, "%s: hiding cap %#x@%#x\n", __func__, + cap, pos); *prev = next; pos = next; continue; @@ -1486,9 +1485,8 @@ static int vfio_cap_init(struct vfio_pci_device *vdev) if (likely(map[pos + i] == PCI_CAP_ID_INVALID)) continue; - pr_warn("%s: %s pci config conflict @0x%x, was cap 0x%x now cap 0x%x\n", - __func__, dev_name(&pdev->dev), - pos + i, map[pos + i], cap); + pci_warn(pdev, "%s: PCI config conflict @%#x, was cap %#x now cap %#x\n", + __func__, pos + i, map[pos + i], cap); } BUILD_BUG_ON(PCI_CAP_ID_MAX >= PCI_CAP_ID_INVALID_VIRT); @@ -1549,8 +1547,8 @@ static int vfio_ecap_init(struct vfio_pci_device *vdev) } if (!len) { - pr_info("%s: %s hiding ecap 0x%x@0x%x\n", - __func__, dev_name(&pdev->dev), ecap, epos); + pci_info(pdev, "%s: hiding ecap %#x@%#x\n", + __func__, ecap, epos); /* If not the first in the chain, we can skip over it */ if (prev) { @@ -1572,9 +1570,8 @@ static int vfio_ecap_init(struct vfio_pci_device *vdev) if (likely(map[epos + i] == PCI_CAP_ID_INVALID)) continue; - pr_warn("%s: %s pci config conflict @0x%x, was ecap 0x%x now ecap 0x%x\n", - __func__, dev_name(&pdev->dev), - epos + i, map[epos + i], ecap); + pci_warn(pdev, "%s: PCI config conflict @%#x, was ecap %#x now ecap %#x\n", + __func__, epos + i, map[epos + i], ecap); } /* diff --git a/drivers/vfio/pci/vfio_pci_nvlink2.c b/drivers/vfio/pci/vfio_pci_nvlink2.c index 32f695ffe128..50fe3c4f7feb 100644 --- a/drivers/vfio/pci/vfio_pci_nvlink2.c +++ b/drivers/vfio/pci/vfio_pci_nvlink2.c @@ -472,6 +472,8 @@ int vfio_pci_ibm_npu2_init(struct vfio_pci_device *vdev) return 0; free_exit: + if (data->base) + memunmap(data->base); kfree(data); return ret; diff --git a/drivers/vfio/platform/Kconfig b/drivers/vfio/platform/Kconfig index bb30128782aa..dc1a3c44f2c6 100644 --- a/drivers/vfio/platform/Kconfig +++ b/drivers/vfio/platform/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config VFIO_PLATFORM tristate "VFIO support for platform devices" depends on VFIO && EVENTFD && (ARM || ARM64) diff --git a/drivers/vfio/platform/reset/Kconfig b/drivers/vfio/platform/reset/Kconfig index 392e3c09def0..1edbe9ee7356 100644 --- a/drivers/vfio/platform/reset/Kconfig +++ b/drivers/vfio/platform/reset/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config VFIO_PLATFORM_CALXEDAXGMAC_RESET tristate "VFIO support for calxeda xgmac reset" depends on VFIO_PLATFORM diff --git a/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c b/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c index 3ddb2704221d..fe95964bc3be 100644 --- a/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c +++ b/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c @@ -89,7 +89,8 @@ static int vfio_platform_amdxgbe_reset(struct vfio_platform_device *vdev) } while ((pcs_value & MDIO_CTRL1_RESET) && --count); if (pcs_value & MDIO_CTRL1_RESET) - pr_warn("%s XGBE PHY reset timeout\n", __func__); + dev_warn(vdev->device, "%s: XGBE PHY reset timeout\n", + __func__); /* disable auto-negotiation */ value = xmdio_read(xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_CTRL1); @@ -114,7 +115,7 @@ static int vfio_platform_amdxgbe_reset(struct vfio_platform_device *vdev) usleep_range(500, 600); if (!count) - pr_warn("%s MAC SW reset failed\n", __func__); + dev_warn(vdev->device, "%s: MAC SW reset failed\n", __func__); return 0; } diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c index c0cd824be2b7..2a45b36bcf58 100644 --- a/drivers/vfio/platform/vfio_platform_common.c +++ b/drivers/vfio/platform/vfio_platform_common.c @@ -12,6 +12,8 @@ * GNU General Public License for more details. */ +#define dev_fmt(fmt) "VFIO: " fmt + #include <linux/device.h> #include <linux/acpi.h> #include <linux/iommu.h> @@ -63,7 +65,7 @@ static int vfio_platform_acpi_probe(struct vfio_platform_device *vdev, adev = ACPI_COMPANION(dev); if (!adev) { - pr_err("VFIO: ACPI companion device not found for %s\n", + dev_err(dev, "ACPI companion device not found for %s\n", vdev->name); return -ENODEV; } @@ -638,7 +640,7 @@ static int vfio_platform_of_probe(struct vfio_platform_device *vdev, ret = device_property_read_string(dev, "compatible", &vdev->compat); if (ret) - pr_err("VFIO: Cannot retrieve compat for %s\n", vdev->name); + dev_err(dev, "Cannot retrieve compat for %s\n", vdev->name); return ret; } @@ -680,14 +682,14 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev, ret = vfio_platform_get_reset(vdev); if (ret && vdev->reset_required) { - pr_err("VFIO: No reset function found for device %s\n", - vdev->name); + dev_err(dev, "No reset function found for device %s\n", + vdev->name); return ret; } group = vfio_iommu_group_get(dev); if (!group) { - pr_err("VFIO: No IOMMU group for device %s\n", vdev->name); + dev_err(dev, "No IOMMU group for device %s\n", vdev->name); ret = -EINVAL; goto put_reset; } diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index a3030cdf3c18..82fcf07fa9ea 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -34,6 +34,7 @@ #include <linux/uaccess.h> #include <linux/vfio.h> #include <linux/wait.h> +#include <linux/sched/signal.h> #define DRIVER_VERSION "0.3" #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>" @@ -704,8 +705,8 @@ static int vfio_group_nb_add_dev(struct vfio_group *group, struct device *dev) return 0; /* TODO Prevent device auto probing */ - WARN(1, "Device %s added to live group %d!\n", dev_name(dev), - iommu_group_id(group->iommu_group)); + dev_WARN(dev, "Device added to live group %d!\n", + iommu_group_id(group->iommu_group)); return 0; } @@ -748,25 +749,22 @@ static int vfio_iommu_group_notifier(struct notifier_block *nb, */ break; case IOMMU_GROUP_NOTIFY_BIND_DRIVER: - pr_debug("%s: Device %s, group %d binding to driver\n", - __func__, dev_name(dev), - iommu_group_id(group->iommu_group)); + dev_dbg(dev, "%s: group %d binding to driver\n", __func__, + iommu_group_id(group->iommu_group)); break; case IOMMU_GROUP_NOTIFY_BOUND_DRIVER: - pr_debug("%s: Device %s, group %d bound to driver %s\n", - __func__, dev_name(dev), - iommu_group_id(group->iommu_group), dev->driver->name); + dev_dbg(dev, "%s: group %d bound to driver %s\n", __func__, + iommu_group_id(group->iommu_group), dev->driver->name); BUG_ON(vfio_group_nb_verify(group, dev)); break; case IOMMU_GROUP_NOTIFY_UNBIND_DRIVER: - pr_debug("%s: Device %s, group %d unbinding from driver %s\n", - __func__, dev_name(dev), - iommu_group_id(group->iommu_group), dev->driver->name); + dev_dbg(dev, "%s: group %d unbinding from driver %s\n", + __func__, iommu_group_id(group->iommu_group), + dev->driver->name); break; case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER: - pr_debug("%s: Device %s, group %d unbound from driver\n", - __func__, dev_name(dev), - iommu_group_id(group->iommu_group)); + dev_dbg(dev, "%s: group %d unbound from driver\n", __func__, + iommu_group_id(group->iommu_group)); /* * XXX An unbound device in a live group is ok, but we'd * really like to avoid the above BUG_ON by preventing other @@ -830,8 +828,8 @@ int vfio_add_group_dev(struct device *dev, device = vfio_group_get_device(group, dev); if (device) { - WARN(1, "Device %s already exists on group %d\n", - dev_name(dev), iommu_group_id(iommu_group)); + dev_WARN(dev, "Device already exists on group %d\n", + iommu_group_id(iommu_group)); vfio_device_put(device); vfio_group_put(group); return -EBUSY; @@ -904,30 +902,17 @@ void *vfio_device_data(struct vfio_device *device) } EXPORT_SYMBOL_GPL(vfio_device_data); -/* Given a referenced group, check if it contains the device */ -static bool vfio_dev_present(struct vfio_group *group, struct device *dev) -{ - struct vfio_device *device; - - device = vfio_group_get_device(group, dev); - if (!device) - return false; - - vfio_device_put(device); - return true; -} - /* * Decrement the device reference count and wait for the device to be * removed. Open file descriptors for the device... */ void *vfio_del_group_dev(struct device *dev) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct vfio_device *device = dev_get_drvdata(dev); struct vfio_group *group = device->group; void *device_data = device->device_data; struct vfio_unbound_dev *unbound; unsigned int i = 0; - long ret; bool interrupted = false; /* @@ -964,6 +949,8 @@ void *vfio_del_group_dev(struct device *dev) * interval with counter to allow the driver to take escalating * measures to release the device if it has the ability to do so. */ + add_wait_queue(&vfio.release_q, &wait); + do { device = vfio_group_get_device(group, dev); if (!device) @@ -975,12 +962,10 @@ void *vfio_del_group_dev(struct device *dev) vfio_device_put(device); if (interrupted) { - ret = wait_event_timeout(vfio.release_q, - !vfio_dev_present(group, dev), HZ * 10); + wait_woken(&wait, TASK_UNINTERRUPTIBLE, HZ * 10); } else { - ret = wait_event_interruptible_timeout(vfio.release_q, - !vfio_dev_present(group, dev), HZ * 10); - if (ret == -ERESTARTSYS) { + wait_woken(&wait, TASK_INTERRUPTIBLE, HZ * 10); + if (signal_pending(current)) { interrupted = true; dev_warn(dev, "Device is currently in use, task" @@ -989,8 +974,10 @@ void *vfio_del_group_dev(struct device *dev) current->comm, task_pid_nr(current)); } } - } while (ret <= 0); + } while (1); + + remove_wait_queue(&vfio.release_q, &wait); /* * In order to support multiple devices per group, devices can be * plucked from the group while other devices in the group are still diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index 8dbb270998f4..40ddc0c5f677 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c @@ -532,7 +532,8 @@ static int tce_iommu_use_page(unsigned long tce, unsigned long *hpa) enum dma_data_direction direction = iommu_tce_direction(tce); if (get_user_pages_fast(tce & PAGE_MASK, 1, - direction != DMA_TO_DEVICE, &page) != 1) + direction != DMA_TO_DEVICE ? FOLL_WRITE : 0, + &page) != 1) return -EFAULT; *hpa = __pa((unsigned long) page_address(page)); @@ -1398,7 +1399,7 @@ unlock_exit: mutex_unlock(&container->lock); } -const struct vfio_iommu_driver_ops tce_iommu_driver_ops = { +static const struct vfio_iommu_driver_ops tce_iommu_driver_ops = { .name = "iommu-vfio-powerpc", .owner = THIS_MODULE, .open = tce_iommu_open, diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 73652e21efec..3ddc375e7063 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -58,12 +58,18 @@ module_param_named(disable_hugepages, MODULE_PARM_DESC(disable_hugepages, "Disable VFIO IOMMU support for IOMMU hugepages."); +static unsigned int dma_entry_limit __read_mostly = U16_MAX; +module_param_named(dma_entry_limit, dma_entry_limit, uint, 0644); +MODULE_PARM_DESC(dma_entry_limit, + "Maximum number of user DMA mappings per container (65535)."); + struct vfio_iommu { struct list_head domain_list; struct vfio_domain *external_domain; /* domain for external user */ struct mutex lock; struct rb_root dma_list; struct blocking_notifier_head notifier; + unsigned int dma_avail; bool v2; bool nesting; }; @@ -91,6 +97,7 @@ struct vfio_dma { struct vfio_group { struct iommu_group *iommu_group; struct list_head next; + bool mdev_group; /* An mdev group */ }; /* @@ -351,7 +358,8 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr, down_read(&mm->mmap_sem); if (mm == current->mm) { - ret = get_user_pages_longterm(vaddr, 1, flags, page, vmas); + ret = get_user_pages(vaddr, 1, flags | FOLL_LONGTERM, page, + vmas); } else { ret = get_user_pages_remote(NULL, mm, vaddr, 1, flags, page, vmas, NULL); @@ -558,7 +566,7 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data, mutex_lock(&iommu->lock); /* Fail if notifier list is empty */ - if ((!iommu->external_domain) || (!iommu->notifier.head)) { + if (!iommu->notifier.head) { ret = -EINVAL; goto pin_done; } @@ -640,11 +648,6 @@ static int vfio_iommu_type1_unpin_pages(void *iommu_data, mutex_lock(&iommu->lock); - if (!iommu->external_domain) { - mutex_unlock(&iommu->lock); - return -EINVAL; - } - do_accounting = !IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu); for (i = 0; i < npage; i++) { struct vfio_dma *dma; @@ -836,6 +839,7 @@ static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma) vfio_unlink_dma(iommu, dma); put_task_struct(dma->task); kfree(dma); + iommu->dma_avail++; } static unsigned long vfio_pgsize_bitmap(struct vfio_iommu *iommu) @@ -1081,12 +1085,18 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, goto out_unlock; } + if (!iommu->dma_avail) { + ret = -ENOSPC; + goto out_unlock; + } + dma = kzalloc(sizeof(*dma), GFP_KERNEL); if (!dma) { ret = -ENOMEM; goto out_unlock; } + iommu->dma_avail--; dma->iova = iova; dma->vaddr = vaddr; dma->prot = prot; @@ -1298,13 +1308,109 @@ static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base) return ret; } +static struct device *vfio_mdev_get_iommu_device(struct device *dev) +{ + struct device *(*fn)(struct device *dev); + struct device *iommu_device; + + fn = symbol_get(mdev_get_iommu_device); + if (fn) { + iommu_device = fn(dev); + symbol_put(mdev_get_iommu_device); + + return iommu_device; + } + + return NULL; +} + +static int vfio_mdev_attach_domain(struct device *dev, void *data) +{ + struct iommu_domain *domain = data; + struct device *iommu_device; + + iommu_device = vfio_mdev_get_iommu_device(dev); + if (iommu_device) { + if (iommu_dev_feature_enabled(iommu_device, IOMMU_DEV_FEAT_AUX)) + return iommu_aux_attach_device(domain, iommu_device); + else + return iommu_attach_device(domain, iommu_device); + } + + return -EINVAL; +} + +static int vfio_mdev_detach_domain(struct device *dev, void *data) +{ + struct iommu_domain *domain = data; + struct device *iommu_device; + + iommu_device = vfio_mdev_get_iommu_device(dev); + if (iommu_device) { + if (iommu_dev_feature_enabled(iommu_device, IOMMU_DEV_FEAT_AUX)) + iommu_aux_detach_device(domain, iommu_device); + else + iommu_detach_device(domain, iommu_device); + } + + return 0; +} + +static int vfio_iommu_attach_group(struct vfio_domain *domain, + struct vfio_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_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 device **old = data, *new; + + new = vfio_mdev_get_iommu_device(dev); + if (!new || (*old && *old != new)) + return -EINVAL; + + *old = new; + + return 0; +} + static int vfio_iommu_type1_attach_group(void *iommu_data, struct iommu_group *iommu_group) { struct vfio_iommu *iommu = iommu_data; struct vfio_group *group; struct vfio_domain *domain, *d; - struct bus_type *bus = NULL, *mdev_bus; + struct bus_type *bus = NULL; int ret; bool resv_msi, msi_remap; phys_addr_t resv_msi_base; @@ -1339,23 +1445,30 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, if (ret) goto out_free; - mdev_bus = symbol_get(mdev_bus_type); + if (vfio_bus_is_mdev(bus)) { + struct device *iommu_device = NULL; - if (mdev_bus) { - if ((bus == mdev_bus) && !iommu_present(bus)) { - symbol_put(mdev_bus_type); + 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) { if (!iommu->external_domain) { INIT_LIST_HEAD(&domain->group_list); iommu->external_domain = domain; - } else + } else { kfree(domain); + } list_add(&group->next, &iommu->external_domain->group_list); mutex_unlock(&iommu->lock); + return 0; } - symbol_put(mdev_bus_type); + + bus = iommu_device->bus; } domain->domain = iommu_domain_alloc(bus); @@ -1373,7 +1486,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, goto out_domain; } - ret = iommu_attach_group(domain->domain, iommu_group); + ret = vfio_iommu_attach_group(domain, group); if (ret) goto out_domain; @@ -1405,8 +1518,8 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, list_for_each_entry(d, &iommu->domain_list, next) { if (d->domain->ops == domain->domain->ops && d->prot == domain->prot) { - iommu_detach_group(domain->domain, iommu_group); - if (!iommu_attach_group(d->domain, 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); @@ -1414,7 +1527,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, return 0; } - ret = iommu_attach_group(domain->domain, iommu_group); + ret = vfio_iommu_attach_group(domain, group); if (ret) goto out_domain; } @@ -1440,7 +1553,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, return 0; out_detach: - iommu_detach_group(domain->domain, iommu_group); + vfio_iommu_detach_group(domain, group); out_domain: iommu_domain_free(domain->domain); out_free: @@ -1531,7 +1644,7 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, if (!group) continue; - iommu_detach_group(domain->domain, iommu_group); + vfio_iommu_detach_group(domain, group); list_del(&group->next); kfree(group); /* @@ -1583,6 +1696,7 @@ static void *vfio_iommu_type1_open(unsigned long arg) INIT_LIST_HEAD(&iommu->domain_list); iommu->dma_list = RB_ROOT; + iommu->dma_avail = dma_entry_limit; mutex_init(&iommu->lock); BLOCKING_INIT_NOTIFIER_HEAD(&iommu->notifier); @@ -1596,7 +1710,7 @@ static void vfio_release_domain(struct vfio_domain *domain, bool external) list_for_each_entry_safe(group, group_tmp, &domain->group_list, next) { if (!external) - iommu_detach_group(domain->domain, group->iommu_group); + vfio_iommu_detach_group(domain, group); list_del(&group->next); kfree(group); } |