Protect incoming preserved devices that were kept running across kexec from being disturbed before userspace either claims them or finishes the Live Update session: * Runtime PM idle D3: In vfio_pci_init_dev(), when pci_liveupdate_is_incoming(pdev) is true, latch vdev->disable_idle_d3 = true and vdev->liveupdate_kept_running = true. Without this, vfio_pci_core_register_device() drops the runtime PM reference and allows the PCI runtime PM core to transition an idle incoming device into D3hot, stopping bus mastering and potentially triggering an internal reset on NoSoftRst- devices. * Non-noiommu boot fallback: In vfio_pci_core_register_device(), after vfio_register_group_dev() determines whether the device is in noiommu mode, if an incoming kept-running device is not in noiommu mode in this kernel, log a pci_warn(), clear PCI_COMMAND_MASTER immediately to stop untranslated DMA, and record liveupdate_stopped_early so finish() resets it without printing a duplicate warning. * Bus/slot reset guards: Refuse vfio_pci_dev_set_needs_reset() and vfio_pci_dev_set_hot_reset() (-EBUSY) while any device in the dev_set has liveupdate_kept_running set, preventing another device in the same dev_set from resetting an unclaimed kept-running device. When an unclaimed device finishes Live Update and no device in its dev_set is open, invoke vfio_pci_dev_set_try_reset() after clearing liveupdate_kept_running so devices without function-level reset still receive a bus/slot reset. * Unbind before finish: In vfio_pci_core_unregister_device(), if a still-unclaimed incoming device is unbound from vfio-pci before the session finishes (or if a concurrent finish() deferred its function reset via liveupdate_reset_pending), clear bus mastering, reset the function and restore config state via vfio_pci_core_liveupdate_reset_locked() under the device lock already held by remove(), and call pci_liveupdate_finish(). Signed-off-by: Vipin Sharma --- drivers/vfio/pci/vfio_pci.c | 1 + drivers/vfio/pci/vfio_pci_core.c | 19 +++++++- drivers/vfio/pci/vfio_pci_liveupdate.c | 16 +++++-- drivers/vfio/pci/vfio_pci_priv.h | 62 ++++++++++++++++++++++++-- include/linux/vfio_pci_core.h | 3 +- 5 files changed, 91 insertions(+), 10 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 9d48b250a5e8..d01b5cc9d4d2 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -139,6 +139,7 @@ static int vfio_pci_init_dev(struct vfio_device *core_vdev) */ vdev->nointxmask = nointxmask; vdev->disable_idle_d3 = disable_idle_d3; + vfio_pci_liveupdate_init_dev(vdev); #ifdef CONFIG_VFIO_PCI_VGA vdev->disable_vga = disable_vga; #endif diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 60ada3bbb6ff..828d620cf836 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -768,6 +768,15 @@ bool vfio_pci_core_liveupdate_reset(struct vfio_pci_core_device *vdev, vdev->reset_works = reset_works; vfio_pci_core_try_reset(vdev); + /* + * Like vfio_pci_core_disable(), try a bus or slot reset for the devices + * in the set that still need one, e.g. this device if it has no usable + * function reset, or others whose reset was deferred while this device + * was kept running. This device is not open, so none in the set may be. + */ + if (!vfio_device_set_open_count(vdev->vdev.dev_set)) + vfio_pci_dev_set_try_reset(vdev->vdev.dev_set); + return !vdev->needs_reset; } EXPORT_SYMBOL_GPL(vfio_pci_core_liveupdate_reset); @@ -2399,6 +2408,8 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev) if (ret) goto out_power; + vfio_pci_liveupdate_check_noiommu(vdev); + vfio_pci_core_debugfs_init(vdev); return 0; @@ -2642,7 +2653,8 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set, list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list) { bool owned; - if (vfio_pci_dev_is_frozen(vdev)) { + if (vfio_pci_dev_is_frozen(vdev) || + vfio_pci_dev_is_kept_running(vdev)) { ret = -EBUSY; break; } @@ -2744,8 +2756,11 @@ static bool vfio_pci_dev_set_needs_reset(struct vfio_device_set *dev_set) if (vfio_device_set_open_count(dev_set) > 1) return false; - list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) + list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) { + if (vfio_pci_dev_is_kept_running(cur)) + return false; needs_reset |= cur->needs_reset; + } return needs_reset; } diff --git a/drivers/vfio/pci/vfio_pci_liveupdate.c b/drivers/vfio/pci/vfio_pci_liveupdate.c index 64810010f983..ad757acac400 100644 --- a/drivers/vfio/pci/vfio_pci_liveupdate.c +++ b/drivers/vfio/pci/vfio_pci_liveupdate.c @@ -457,7 +457,7 @@ vfio_pci_liveupdate_attach_incoming(struct vfio_pci_core_device *vdev, { guard(mutex)(&vdev->vdev.dev_set->lock); - if (vdev->liveupdate_incoming || vdev->liveupdate_claimed || + if (vdev->liveupdate_incoming || !vdev->liveupdate_kept_running || !pci_liveupdate_is_incoming(vdev->pdev)) return -EBUSY; @@ -558,8 +558,7 @@ static void vfio_pci_liveupdate_finish_vdev(struct vfio_pci_core_device *vdev, bool was_reset; scoped_guard(mutex, &vdev->vdev.dev_set->lock) { - if (!vdev->liveupdate_claimed && - pci_liveupdate_is_incoming(pdev)) { + if (vdev->liveupdate_kept_running) { if (vdev->liveupdate_incoming) { saved_state = vdev->liveupdate_incoming->saved_state; vdev->liveupdate_incoming->saved_state = NULL; @@ -572,6 +571,17 @@ static void vfio_pci_liveupdate_finish_vdev(struct vfio_pci_core_device *vdev, saved_state = NULL; } + /* + * The device is stopped below, so stop shielding it from + * the dev_set resets, including the fallback done by + * vfio_pci_core_liveupdate_reset(). + */ + vdev->liveupdate_kept_running = false; + + /* A device stopped early was logged when it was stopped. */ + if (vdev->liveupdate_stopped_early) + reason = NULL; + /* * An unbind of the device waits for the reference held * by finish() with the device lock held, so the reset diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h index d8670dc51bb4..39ea26402312 100644 --- a/drivers/vfio/pci/vfio_pci_priv.h +++ b/drivers/vfio/pci/vfio_pci_priv.h @@ -169,6 +169,40 @@ static inline bool vfio_pci_dev_is_frozen(struct vfio_pci_core_device *vdev) return smp_load_acquire(&vdev->liveupdate_frozen); } +static inline bool vfio_pci_dev_is_kept_running(struct vfio_pci_core_device *vdev) +{ + return vdev->liveupdate_kept_running; +} + +static inline void vfio_pci_liveupdate_init_dev(struct vfio_pci_core_device *vdev) +{ + struct pci_dev *pdev = to_pci_dev(vdev->vdev.dev); + + if (pci_liveupdate_is_incoming(pdev)) { + vdev->disable_idle_d3 = true; + vdev->liveupdate_kept_running = true; + } +} + +/* + * Called by probe once the device is registered, with the device lock held. + * retrieve() and finish() look the device up with that lock held, see + * vfio_pci_liveupdate_find_probed(), so they see the result of this check. + */ +static inline void +vfio_pci_liveupdate_check_noiommu(struct vfio_pci_core_device *vdev) +{ + guard(mutex)(&vdev->vdev.dev_set->lock); + + if (vdev->liveupdate_kept_running && + !vfio_device_is_noiommu(&vdev->vdev)) { + pci_warn(vdev->pdev, + "Stopping device kept running across Live Update: not in noiommu mode\n"); + pci_clear_master(vdev->pdev); + vdev->liveupdate_stopped_early = true; + } +} + static inline void vfio_pci_liveupdate_free_incoming(struct vfio_pci_core_device *vdev) { @@ -196,7 +230,7 @@ vfio_pci_liveupdate_claim_incoming(struct vfio_pci_core_device *vdev) return; vfio_pci_liveupdate_free_incoming(vdev); - vdev->liveupdate_claimed = true; + vdev->liveupdate_kept_running = false; } static inline void @@ -206,11 +240,17 @@ vfio_pci_liveupdate_unregister_dev(struct vfio_pci_core_device *vdev) scoped_guard(mutex, &vdev->vdev.dev_set->lock) { /* - * Complete a reset that finish() could not do, see - * vfio_pci_liveupdate_finish_vdev(). + * Reset a device unbound before finish(), or whose reset + * finish() could not do, see vfio_pci_liveupdate_finish_vdev(). */ - if (vdev->liveupdate_reset_pending) { + if (vdev->liveupdate_kept_running || + vdev->liveupdate_reset_pending) { + if (vdev->liveupdate_kept_running && + !vdev->liveupdate_stopped_early) + pci_warn(pdev, + "Resetting device not taken over after Live Update (vfio-pci unbound)\n"); vfio_pci_core_liveupdate_reset_locked(vdev); + vdev->liveupdate_kept_running = false; vdev->liveupdate_reset_pending = false; } vfio_pci_liveupdate_free_incoming(vdev); @@ -234,6 +274,20 @@ static inline bool vfio_pci_dev_is_frozen(struct vfio_pci_core_device *vdev) return false; } +static inline bool vfio_pci_dev_is_kept_running(struct vfio_pci_core_device *vdev) +{ + return false; +} + +static inline void vfio_pci_liveupdate_init_dev(struct vfio_pci_core_device *vdev) +{ +} + +static inline void +vfio_pci_liveupdate_check_noiommu(struct vfio_pci_core_device *vdev) +{ +} + static inline void vfio_pci_liveupdate_free_incoming(struct vfio_pci_core_device *vdev) { diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index f5c279d0df1e..9bdbc8099bda 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -152,7 +152,8 @@ struct vfio_pci_core_device { #ifdef CONFIG_VFIO_PCI_LIVEUPDATE bool liveupdate_frozen; bool liveupdate_intx_disabled; - bool liveupdate_claimed; + bool liveupdate_kept_running; + bool liveupdate_stopped_early; bool liveupdate_reset_pending; struct vfio_pci_liveupdate_incoming *liveupdate_incoming; #endif -- 2.56.0.317.g454f8302e1-goog