diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/vfio/mdev/mdev_core.c | 16 | ||||
-rw-r--r-- | drivers/vfio/mdev/mdev_private.h | 5 | ||||
-rw-r--r-- | drivers/vfio/mdev/mdev_sysfs.c | 6 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 90 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_config.c | 2 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_private.h | 6 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/Makefile | 2 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/vfio_platform_amdxgbe.c | 2 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/vfio_platform_bcmflexrm.c | 2 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c | 2 | ||||
-rw-r--r-- | drivers/vfio/vfio.c | 8 | ||||
-rw-r--r-- | drivers/vfio/vfio_iommu_spapr_tce.c | 3 |
12 files changed, 107 insertions, 37 deletions
diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c index 0212f0ee8aea..b96fedc77ee5 100644 --- a/drivers/vfio/mdev/mdev_core.c +++ b/drivers/vfio/mdev/mdev_core.c @@ -60,9 +60,9 @@ struct mdev_device *mdev_from_dev(struct device *dev) } EXPORT_SYMBOL(mdev_from_dev); -uuid_le mdev_uuid(struct mdev_device *mdev) +const guid_t *mdev_uuid(struct mdev_device *mdev) { - return mdev->uuid; + return &mdev->uuid; } EXPORT_SYMBOL(mdev_uuid); @@ -88,8 +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 inline struct mdev_parent *mdev_get_parent(struct mdev_parent *parent) { if (parent) kref_get(&parent->ref); @@ -276,7 +275,8 @@ static void mdev_device_release(struct device *dev) kfree(mdev); } -int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid) +int mdev_device_create(struct kobject *kobj, + struct device *dev, const guid_t *uuid) { int ret; struct mdev_device *mdev, *tmp; @@ -291,7 +291,7 @@ int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid) /* Check for duplicate */ list_for_each_entry(tmp, &mdev_list, next) { - if (!uuid_le_cmp(tmp->uuid, uuid)) { + if (guid_equal(&tmp->uuid, uuid)) { mutex_unlock(&mdev_list_lock); ret = -EEXIST; goto mdev_fail; @@ -305,7 +305,7 @@ int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid) goto mdev_fail; } - memcpy(&mdev->uuid, &uuid, sizeof(uuid_le)); + guid_copy(&mdev->uuid, uuid); list_add(&mdev->next, &mdev_list); mutex_unlock(&mdev_list_lock); @@ -315,7 +315,7 @@ int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid) mdev->dev.parent = dev; mdev->dev.bus = &mdev_bus_type; mdev->dev.release = mdev_device_release; - dev_set_name(&mdev->dev, "%pUl", uuid.b); + dev_set_name(&mdev->dev, "%pUl", uuid); ret = device_register(&mdev->dev); if (ret) { diff --git a/drivers/vfio/mdev/mdev_private.h b/drivers/vfio/mdev/mdev_private.h index b5819b7d7ef7..379758c52b1b 100644 --- a/drivers/vfio/mdev/mdev_private.h +++ b/drivers/vfio/mdev/mdev_private.h @@ -28,7 +28,7 @@ struct mdev_parent { struct mdev_device { struct device dev; struct mdev_parent *parent; - uuid_le uuid; + guid_t uuid; void *driver_data; struct kref ref; struct list_head next; @@ -58,7 +58,8 @@ void parent_remove_sysfs_files(struct mdev_parent *parent); int mdev_create_sysfs_files(struct device *dev, struct mdev_type *type); void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type); -int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid); +int mdev_device_create(struct kobject *kobj, + struct device *dev, const guid_t *uuid); int mdev_device_remove(struct device *dev, bool force_remove); #endif /* MDEV_PRIVATE_H */ diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c index ce5dd219f2c8..5193a0e0ce5a 100644 --- a/drivers/vfio/mdev/mdev_sysfs.c +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -55,7 +55,7 @@ static ssize_t create_store(struct kobject *kobj, struct device *dev, const char *buf, size_t count) { char *str; - uuid_le uuid; + guid_t uuid; int ret; if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1)) @@ -65,12 +65,12 @@ static ssize_t create_store(struct kobject *kobj, struct device *dev, if (!str) return -ENOMEM; - ret = uuid_le_to_bin(str, &uuid); + ret = guid_parse(str, &uuid); kfree(str); if (ret) return ret; - ret = mdev_device_create(kobj, dev, uuid); + ret = mdev_device_create(kobj, dev, &uuid); if (ret) return ret; diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index ff60bd1ea587..a25659b5a5d1 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -209,6 +209,57 @@ static bool vfio_pci_nointx(struct pci_dev *pdev) return false; } +static void vfio_pci_probe_power_state(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u16 pmcsr; + + if (!pdev->pm_cap) + return; + + pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &pmcsr); + + vdev->needs_pm_restore = !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET); +} + +/* + * pci_set_power_state() wrapper handling devices which perform a soft reset on + * D3->D0 transition. Save state prior to D0/1/2->D3, stash it on the vdev, + * restore when returned to D0. Saved separately from pci_saved_state for use + * by PM capability emulation and separately from pci_dev internal saved state + * to avoid it being overwritten and consumed around other resets. + */ +int vfio_pci_set_power_state(struct vfio_pci_device *vdev, pci_power_t state) +{ + struct pci_dev *pdev = vdev->pdev; + bool needs_restore = false, needs_save = false; + int ret; + + if (vdev->needs_pm_restore) { + if (pdev->current_state < PCI_D3hot && state >= PCI_D3hot) { + pci_save_state(pdev); + needs_save = true; + } + + if (pdev->current_state >= PCI_D3hot && state <= PCI_D0) + needs_restore = true; + } + + ret = pci_set_power_state(pdev, state); + + if (!ret) { + /* D3 might be unsupported via quirk, skip unless in D3 */ + if (needs_save && pdev->current_state >= PCI_D3hot) { + vdev->pm_save = pci_store_saved_state(pdev); + } else if (needs_restore) { + pci_load_and_free_saved_state(pdev, &vdev->pm_save); + pci_restore_state(pdev); + } + } + + return ret; +} + static int vfio_pci_enable(struct vfio_pci_device *vdev) { struct pci_dev *pdev = vdev->pdev; @@ -216,7 +267,7 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) u16 cmd; u8 msix_pos; - pci_set_power_state(pdev, PCI_D0); + vfio_pci_set_power_state(vdev, PCI_D0); /* Don't allow our initial saved state to include busmaster */ pci_clear_master(pdev); @@ -407,7 +458,7 @@ out: vfio_pci_try_bus_reset(vdev); if (!disable_idle_d3) - pci_set_power_state(pdev, PCI_D3hot); + vfio_pci_set_power_state(vdev, PCI_D3hot); } static void vfio_pci_release(void *device_data) @@ -708,6 +759,7 @@ static long vfio_pci_ioctl(void *device_data, { void __iomem *io; size_t size; + u16 orig_cmd; info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); info.flags = 0; @@ -723,15 +775,23 @@ static long vfio_pci_ioctl(void *device_data, break; } - /* Is it really there? */ + /* + * Is it really there? Enable memory decode for + * implicit access in pci_map_rom(). + */ + pci_read_config_word(pdev, PCI_COMMAND, &orig_cmd); + pci_write_config_word(pdev, PCI_COMMAND, + orig_cmd | PCI_COMMAND_MEMORY); + io = pci_map_rom(pdev, &size); - if (!io || !size) { + if (io) { + info.flags = VFIO_REGION_INFO_FLAG_READ; + pci_unmap_rom(pdev, io); + } else { info.size = 0; - break; } - pci_unmap_rom(pdev, io); - info.flags = VFIO_REGION_INFO_FLAG_READ; + pci_write_config_word(pdev, PCI_COMMAND, orig_cmd); break; } case VFIO_PCI_VGA_REGION_INDEX: @@ -1286,6 +1346,8 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) vfio_pci_set_vga_decode(vdev, false)); } + vfio_pci_probe_power_state(vdev); + if (!disable_idle_d3) { /* * pci-core sets the device power state to an unknown value at @@ -1296,8 +1358,8 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) * be able to get to D3. Therefore first do a D0 transition * before going to D3. */ - pci_set_power_state(pdev, PCI_D0); - pci_set_power_state(pdev, PCI_D3hot); + vfio_pci_set_power_state(vdev, PCI_D0); + vfio_pci_set_power_state(vdev, PCI_D3hot); } return ret; @@ -1316,6 +1378,11 @@ static void vfio_pci_remove(struct pci_dev *pdev) vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev); kfree(vdev->region); mutex_destroy(&vdev->ioeventfds_lock); + + if (!disable_idle_d3) + vfio_pci_set_power_state(vdev, PCI_D0); + + kfree(vdev->pm_save); kfree(vdev); if (vfio_pci_is_vga(pdev)) { @@ -1324,9 +1391,6 @@ static void vfio_pci_remove(struct pci_dev *pdev) VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM | VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM); } - - if (!disable_idle_d3) - pci_set_power_state(pdev, PCI_D0); } static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev, @@ -1551,7 +1615,7 @@ put_devs: tmp->needs_reset = false; if (tmp != vdev && !disable_idle_d3) - pci_set_power_state(tmp->pdev, PCI_D3hot); + vfio_pci_set_power_state(tmp, PCI_D3hot); } vfio_device_put(devs.devices[i]); diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 423ea1f98441..e82b51114687 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -691,7 +691,7 @@ static int vfio_pm_config_write(struct vfio_pci_device *vdev, int pos, break; } - pci_set_power_state(vdev->pdev, state); + vfio_pci_set_power_state(vdev, state); } return count; diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index 8c0009f00818..1812cf22fc4f 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -114,7 +114,9 @@ struct vfio_pci_device { bool has_vga; bool needs_reset; bool nointx; + bool needs_pm_restore; struct pci_saved_state *pci_saved_state; + struct pci_saved_state *pm_save; struct vfio_pci_reflck *reflck; int refcnt; int ioeventfds_nr; @@ -161,6 +163,10 @@ extern int vfio_pci_register_dev_region(struct vfio_pci_device *vdev, unsigned int type, unsigned int subtype, const struct vfio_pci_regops *ops, size_t size, u32 flags, void *data); + +extern int vfio_pci_set_power_state(struct vfio_pci_device *vdev, + pci_power_t state); + #ifdef CONFIG_VFIO_PCI_IGD extern int vfio_pci_igd_init(struct vfio_pci_device *vdev); #else diff --git a/drivers/vfio/platform/reset/Makefile b/drivers/vfio/platform/reset/Makefile index 57abd4f0ac5b..7294c5ea122e 100644 --- a/drivers/vfio/platform/reset/Makefile +++ b/drivers/vfio/platform/reset/Makefile @@ -2,8 +2,6 @@ vfio-platform-calxedaxgmac-y := vfio_platform_calxedaxgmac.o vfio-platform-amdxgbe-y := vfio_platform_amdxgbe.o -ccflags-y += -Idrivers/vfio/platform - obj-$(CONFIG_VFIO_PLATFORM_CALXEDAXGMAC_RESET) += vfio-platform-calxedaxgmac.o obj-$(CONFIG_VFIO_PLATFORM_AMDXGBE_RESET) += vfio-platform-amdxgbe.o obj-$(CONFIG_VFIO_PLATFORM_BCMFLEXRM_RESET) += vfio_platform_bcmflexrm.o diff --git a/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c b/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c index bcd419cfd79c..3ddb2704221d 100644 --- a/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c +++ b/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c @@ -25,7 +25,7 @@ #include <uapi/linux/mdio.h> #include <linux/delay.h> -#include "vfio_platform_private.h" +#include "../vfio_platform_private.h" #define DMA_MR 0x3000 #define MAC_VR 0x0110 diff --git a/drivers/vfio/platform/reset/vfio_platform_bcmflexrm.c b/drivers/vfio/platform/reset/vfio_platform_bcmflexrm.c index d45c3be71198..16165a62b86d 100644 --- a/drivers/vfio/platform/reset/vfio_platform_bcmflexrm.c +++ b/drivers/vfio/platform/reset/vfio_platform_bcmflexrm.c @@ -23,7 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> -#include "vfio_platform_private.h" +#include "../vfio_platform_private.h" /* FlexRM configuration */ #define RING_REGS_SIZE 0x10000 diff --git a/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c b/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c index 49e5df6e8f29..e0356de5df54 100644 --- a/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c +++ b/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c @@ -24,7 +24,7 @@ #include <linux/init.h> #include <linux/io.h> -#include "vfio_platform_private.h" +#include "../vfio_platform_private.h" #define DRIVER_VERSION "0.1" #define DRIVER_AUTHOR "Eric Auger <eric.auger@linaro.org>" diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 64833879f75d..a3030cdf3c18 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -2219,12 +2219,12 @@ static int __init vfio_init(void) vfio.class->devnode = vfio_devnode; - ret = alloc_chrdev_region(&vfio.group_devt, 0, MINORMASK, "vfio"); + ret = alloc_chrdev_region(&vfio.group_devt, 0, MINORMASK + 1, "vfio"); if (ret) goto err_alloc_chrdev; cdev_init(&vfio.group_cdev, &vfio_group_fops); - ret = cdev_add(&vfio.group_cdev, vfio.group_devt, MINORMASK); + ret = cdev_add(&vfio.group_cdev, vfio.group_devt, MINORMASK + 1); if (ret) goto err_cdev_add; @@ -2236,7 +2236,7 @@ static int __init vfio_init(void) return 0; err_cdev_add: - unregister_chrdev_region(vfio.group_devt, MINORMASK); + unregister_chrdev_region(vfio.group_devt, MINORMASK + 1); err_alloc_chrdev: class_destroy(vfio.class); vfio.class = NULL; @@ -2254,7 +2254,7 @@ static void __exit vfio_cleanup(void) #endif idr_destroy(&vfio.group_idr); cdev_del(&vfio.group_cdev); - unregister_chrdev_region(vfio.group_devt, MINORMASK); + unregister_chrdev_region(vfio.group_devt, MINORMASK + 1); class_destroy(vfio.class); vfio.class = NULL; misc_deregister(&vfio_dev); diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index c424913324e3..8dbb270998f4 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c @@ -1235,7 +1235,8 @@ static void tce_iommu_release_ownership_ddw(struct tce_container *container, } for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) - table_group->ops->unset_window(table_group, i); + if (container->tables[i]) + table_group->ops->unset_window(table_group, i); table_group->ops->release_ownership(table_group); } |