From: <mhonap@nvidia.com>
To: <alex@shazbot.org>, <jgg@ziepe.ca>, <ankita@nvidia.com>,
<jic23@kernel.org>, <dave.jiang@intel.com>,
<alejandro.lucero-palau@amd.com>, <smadhavan@nvidia.com>,
<corbet@lwn.net>, <skhan@linuxfoundation.org>,
<dave@stgolabs.net>, <alison.schofield@intel.com>,
<vishal.l.verma@intel.com>, <iweiny@kernel.org>,
<ming.li@zohomail.com>, <yishaih@nvidia.com>,
<skolothumtho@nvidia.com>, <kevin.tian@intel.com>,
<bhelgaas@google.com>, <dmatlack@google.com>, <kees@kernel.org>,
<gustavoars@kernel.org>
Cc: <cjia@nvidia.com>, <kjaju@nvidia.com>, <vsethi@nvidia.com>,
<zhiw@nvidia.com>, <mhonap@nvidia.com>,
<linux-doc@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<kvm@vger.kernel.org>, <linux-cxl@vger.kernel.org>,
<linux-pci@vger.kernel.org>, <linux-kselftest@vger.kernel.org>,
<linux-hardening@vger.kernel.org>
Subject: [PATCH v4 24/27] vfio/cxl: Service a guest-triggered CXL reset
Date: Thu, 13 Aug 2026 15:06:28 +0530 [thread overview]
Message-ID: <20260813093631.2288172-25-mhonap@nvidia.com> (raw)
In-Reply-To: <20260813093631.2288172-1-mhonap@nvidia.com>
From: Manish Honap <mhonap@nvidia.com>
When the guest sets Initiate_CXL_Reset in the CXL DVSEC, run the reset on
its behalf. The bit is not forwarded to hardware; the CXL core drives the
state machine through cxl_reset_dvsec_sequence(). The memory_lock, mapping
revoke and dma-buf quiesce are core-internal, so the core exposes a
wrapper that runs them around the CXL-specific work.
A CXL reset can clear config like an FLR, so the sequence is bracketed by
pci_save_state() and pci_restore_state(), with Bus Master dropped for the
window and kept dropped if a step failed, so the function cannot DMA over
decoders that were not restored. The guest owns Mem_Clr_Enable, read from
the shadow, and the outcome is stamped into STATUS2 for it to poll.
Signed-off-by: Manish Honap <mhonap@nvidia.com>
---
drivers/vfio/pci/cxl/vfio_cxl_core.c | 167 ++++++++++++++++++++-
drivers/vfio/pci/vfio_pci_config.c | 4 +-
drivers/vfio/pci/vfio_pci_core.c | 215 +++++++++++++++++++++++++--
drivers/vfio/pci/vfio_pci_priv.h | 2 +
include/linux/vfio_pci_core.h | 3 +
5 files changed, 374 insertions(+), 17 deletions(-)
diff --git a/drivers/vfio/pci/cxl/vfio_cxl_core.c b/drivers/vfio/pci/cxl/vfio_cxl_core.c
index f45eaa60bad2..b3eaefa03479 100644
--- a/drivers/vfio/pci/cxl/vfio_cxl_core.c
+++ b/drivers/vfio/pci/cxl/vfio_cxl_core.c
@@ -7,6 +7,7 @@
#include <linux/cleanup.h>
#include <linux/memory-failure.h>
+#include <linux/memregion.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -761,6 +762,16 @@ static u16 vfio_cxl_dvsec16(struct vfio_cxl_state *cxl, u32 off)
return (dw >> (8 * (off % sizeof(u32)))) & 0xffff;
}
+/* Write a 16-bit DVSEC field into the shadow; the field must not straddle a dword. */
+static void vfio_cxl_dvsec_write16(struct vfio_cxl_state *cxl, u32 off, u16 val)
+{
+ u32 shift = 8 * (off % sizeof(u32));
+ u32 idx = off / sizeof(u32);
+
+ cxl->dvsec_shadow[idx] &= ~(0xffffU << shift);
+ cxl->dvsec_shadow[idx] |= (u32)val << shift;
+}
+
/*
* Apply the CXL r4.0 8.1.3 write class for the 16-bit DVSEC register at @off.
* Control is programmable, Status is write-1-to-clear, and Capability, Lock and
@@ -815,12 +826,14 @@ static int vfio_cxl_config_write(struct vfio_pci_core_device *vdev, int pos,
int boff = (pos - cxl->dvsec) % sizeof(u32);
u32 off = idx * sizeof(u32);
__le32 le_wval = 0, le_wmask = 0;
+ u16 before, after, lo, hi;
u32 old, wval, wmask;
- u16 lo, hi;
if (pos < cxl->dvsec || pos >= cxl->dvsec + cxl->dvsec_len)
return -ENODEV;
+ before = vfio_cxl_dvsec16(cxl, PCI_DVSEC_CXL_CTRL2);
+
/*
* Place the guest bytes and a matching byte mask at the write offset,
* then let the per-field class decide what actually lands in the shadow.
@@ -836,9 +849,159 @@ static int vfio_cxl_config_write(struct vfio_pci_core_device *vdev, int pos,
hi = vfio_cxl_dvsec_field(off + 2, old >> 16, wval >> 16, wmask >> 16);
cxl->dvsec_shadow[idx] = lo | ((u32)hi << 16);
+ /*
+ * A 0->1 write of Initiate_CXL_Reset asks for a CXL reset. It is not
+ * forwarded to hardware; cxl_reset_dvsec_sequence() drives the state
+ * machine, and the outcome comes back through STATUS2.
+ */
+ after = vfio_cxl_dvsec16(cxl, PCI_DVSEC_CXL_CTRL2);
+ if (!(before & PCI_DVSEC_CXL_INIT_CXL_RST) &&
+ (after & PCI_DVSEC_CXL_INIT_CXL_RST) &&
+ cxl_reset_capable(vdev->pdev))
+ vfio_pci_core_cxl_reset(vdev);
+
return count;
}
+static int vfio_cxl_reset(struct vfio_pci_core_device *vdev)
+{
+ struct vfio_cxl_state *cxl = vdev->cxl;
+ struct pci_dev *pdev = vdev->pdev;
+ struct pci_saved_state *saved_state;
+ bool mem_clr;
+ u16 ctrl2, status2, cmd;
+ int ret;
+
+ lockdep_assert_held_write(&vdev->memory_lock);
+
+ /*
+ * The host cxl_reset PCI method cannot run for a vfio-owned device: it
+ * requests the HDM range that this driver already holds exclusively, so
+ * it always fails busy. Drive the DVSEC reset directly here. Report
+ * not-capable so the core reset path can fall back to a standard PCI
+ * reset for a device with no CXL reset, such as a multifunction device.
+ */
+ if (!cxl_reset_capable(pdev))
+ return -ENOTTY;
+
+ /*
+ * Mem_Clr_En comes from the guest shadow CTRL2 while the device is open.
+ * The core reset path also drives this reset at VM power on and off with
+ * no shadow present, so read it from the live DVSEC then.
+ */
+ if (cxl->dvsec_shadow)
+ ctrl2 = vfio_cxl_dvsec16(cxl, PCI_DVSEC_CXL_CTRL2);
+ else
+ pci_read_config_word(pdev, cxl->dvsec + PCI_DVSEC_CXL_CTRL2,
+ &ctrl2);
+ mem_clr = ctrl2 & PCI_DVSEC_CXL_RST_MEM_CLR_EN;
+
+ /*
+ * Mem_Clr is guest-controlled (Mem_Clr_En in the DVSEC CTRL2), so honor it
+ * and pass it to cxl_reset_dvsec_sequence(). It zeroes the device memory,
+ * but that sequence does not write back host CPU caches over the HDM range
+ * the way the host cxl_reset method does. The range is mapped write-back,
+ * so a dirty host line could survive the clear and overwrite it; invalidate
+ * it first when the platform can.
+ *
+ * When cpu_cache_has_invalidate_memregion() is false proceed rather
+ * than abort. Unlike native CXL region invalidation, which the host
+ * manages and must fail safe, the host CPU never writes passthrough
+ * HDM range: the guest owns it through its stage-2 mapping, so there
+ * are no dirty host lines for the clear to lose.
+ * Warn once so the case is visible rather than silent.
+ */
+ if (mem_clr) {
+ if (cpu_cache_has_invalidate_memregion()) {
+ ret = cpu_cache_invalidate_memregion(cxl->hpa_range.start,
+ range_len(&cxl->hpa_range));
+ if (ret) {
+ pci_err(pdev, "vfio-cxl: reset: CPU cache invalidate failed (%d), aborting reset\n",
+ ret);
+ return ret;
+ }
+ } else {
+ pci_warn_once(pdev, "vfio-cxl: reset: no CPU cache invalidation available; proceeding with Mem_Clr (host does not cache the HDM range)\n");
+ }
+ }
+
+ /*
+ * A CXL reset can clear config like an FLR, so save state and drop Bus
+ * Master for the reset window; the function masters the bus again only
+ * once the decoders are restored.
+ *
+ * Sample the guest's Bus Master intent from live config before the reset
+ * perturbs it, so it can be reapplied after the decoder is known good with
+ * no window in which pci_restore_state() leaves Bus Master enabled.
+ */
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+
+ saved_state = pci_store_saved_state(pdev);
+ if (!saved_state && pdev->state_saved) {
+ pci_err(pdev, "vfio-cxl: reset: saved-state stash failed (-ENOMEM), aborting reset\n");
+ return -ENOMEM;
+ }
+ if (saved_state)
+ pci_save_state(pdev);
+ pci_clear_master(pdev);
+ ret = cxl_reset_dvsec_sequence(pdev, mem_clr);
+ pci_restore_state(pdev);
+ pci_clear_master(pdev);
+ /*
+ * pci_restore_state() reinstated the guest's PCI_COMMAND, which may have
+ * re-enabled Bus Master while the reset outcome, and thus decoder
+ * validity, is not yet known. The pci_clear_master() above is its
+ * immediate next statement, so there is no window in which a failed reset
+ * could DMA over decoders that were not restored.
+ */
+ if (saved_state)
+ pci_load_and_free_saved_state(pdev, &saved_state);
+ /*
+ * Re-enable Bus Master only for a clean reset or -EBUSY (the reset never
+ * ran, so the firmware-committed decoder is intact), and only if the guest
+ * had it enabled (sampled before the reset).
+ */
+ if ((!ret || ret == -EBUSY) && (cmd & PCI_COMMAND_MASTER))
+ pci_set_master(pdev);
+
+ vfio_cxl_post_reset(vdev);
+
+ /*
+ * A clean reset restored the decoder, and -EBUSY means the reset never
+ * ran so the firmware-committed decoder is intact: both are known-good.
+ * Any other error may have left the decoder unrestored, so close the gate
+ * until the next open or restore. Faults cannot race this: the caller
+ * holds memory_lock for write across the whole reset.
+ */
+ if (cxl->hdm_shadow) {
+ if (!ret || ret == -EBUSY)
+ cxl->hdm_valid = true;
+ else
+ cxl->hdm_valid = false;
+ }
+
+ /*
+ * The guest-facing DVSEC bookkeeping only applies while the device is
+ * open. Initiate_CXL_Reset self-clears in hardware; mirror that and
+ * stamp the outcome onto a fresh hardware STATUS2 read for the polling
+ * guest. A contended -EBUSY reports as an error so the guest can reissue
+ * rather than poll a result that never comes.
+ */
+ if (cxl->dvsec_shadow) {
+ vfio_cxl_dvsec_write16(cxl, PCI_DVSEC_CXL_CTRL2,
+ vfio_cxl_dvsec16(cxl, PCI_DVSEC_CXL_CTRL2) &
+ ~PCI_DVSEC_CXL_INIT_CXL_RST);
+
+ pci_read_config_word(pdev, cxl->dvsec + PCI_DVSEC_CXL_STATUS2,
+ &status2);
+ status2 &= ~(PCI_DVSEC_CXL_RST_DONE | PCI_DVSEC_CXL_RST_ERR);
+ status2 |= ret ? PCI_DVSEC_CXL_RST_ERR : PCI_DVSEC_CXL_RST_DONE;
+ vfio_cxl_dvsec_write16(cxl, PCI_DVSEC_CXL_STATUS2, status2);
+ }
+
+ return ret;
+}
+
static const struct vfio_cxl_ops vfio_cxl_ops = {
.init_device = vfio_cxl_init_device,
.release_device = vfio_cxl_release_device,
@@ -849,6 +1012,7 @@ static const struct vfio_cxl_ops vfio_cxl_ops = {
.zap = vfio_cxl_zap,
.post_reset = vfio_cxl_post_reset,
.pm_restore = vfio_cxl_pm_restore,
+ .reset = vfio_cxl_reset,
.owner = THIS_MODULE,
};
@@ -869,3 +1033,4 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VFIO support for CXL Type-2 devices");
MODULE_ALIAS("vfio-cxl");
MODULE_IMPORT_NS("CXL");
+MODULE_IMPORT_NS("DEVMEM");
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index 01d808546a4c..f6509824988c 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -910,7 +910,7 @@ static int vfio_exp_config_write(struct vfio_pci_core_device *vdev, int pos,
if (!ret && (cap & PCI_EXP_DEVCAP_FLR)) {
vfio_pci_zap_and_down_write_memory_lock(vdev);
vfio_pci_dma_buf_move(vdev, true);
- pci_try_reset_function(vdev->pdev);
+ vfio_pci_reset_function(vdev);
vfio_pci_cxl_post_reset(vdev);
if (__vfio_pci_memory_enabled(vdev))
vfio_pci_dma_buf_move(vdev, false);
@@ -996,7 +996,7 @@ static int vfio_af_config_write(struct vfio_pci_core_device *vdev, int pos,
if (!ret && (cap & PCI_AF_CAP_FLR) && (cap & PCI_AF_CAP_TP)) {
vfio_pci_zap_and_down_write_memory_lock(vdev);
vfio_pci_dma_buf_move(vdev, true);
- pci_try_reset_function(vdev->pdev);
+ vfio_pci_reset_function(vdev);
vfio_pci_cxl_post_reset(vdev);
if (__vfio_pci_memory_enabled(vdev))
vfio_pci_dma_buf_move(vdev, false);
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index fc8235c8b4fc..0fed8e00bc1d 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -635,8 +635,26 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
goto out_power;
/* If reset fails because of the device lock, fail this path entirely */
- ret = pci_try_reset_function(pdev);
- if (ret == -EAGAIN)
+ if (vdev->cxl_ops && vdev->cxl_ops->reset) {
+ /*
+ * VM power-on resets a CXL Type-2 device through its DVSEC
+ * sequence. vconfig is not built yet here, so take memory_lock
+ * and call the op directly rather than the wrapper.
+ */
+ down_write(&vdev->memory_lock);
+ ret = vdev->cxl_ops->reset(vdev);
+ up_write(&vdev->memory_lock);
+ } else {
+ ret = pci_try_reset_function(pdev);
+ }
+ /*
+ * -EAGAIN means the reset could not run. For a CXL device any reset
+ * error must also fail the open: a failed DVSEC reset can leave the HDM
+ * decoder cleared or unrestored, and continuing would expose the HDM
+ * region for host access through a decoder in an unknown state.
+ */
+ if (ret == -EAGAIN ||
+ (vdev->cxl_ops && vdev->cxl_ops->reset && ret))
goto out_disable_device;
vdev->reset_works = !ret;
@@ -824,16 +842,30 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
* overwrite the previously restored configuration information.
*/
if (vdev->reset_works) {
- bridge = pci_upstream_bridge(pdev);
- if (bridge && !pci_dev_trylock(bridge))
- goto out_restore_state;
- if (pci_dev_trylock(pdev)) {
- if (!__pci_reset_function_locked(pdev))
+ if (vdev->cxl_ops && vdev->cxl_ops->reset) {
+ /*
+ * VM power-off resets a CXL Type-2 device through its
+ * DVSEC sequence. The sequence takes its own device lock,
+ * so run it outside the lock below.
+ * vconfig is already freed here, so call the op directly
+ * under memory_lock rather than the wrapper.
+ */
+ down_write(&vdev->memory_lock);
+ if (!vdev->cxl_ops->reset(vdev))
vdev->needs_reset = false;
- pci_dev_unlock(pdev);
+ up_write(&vdev->memory_lock);
+ } else {
+ bridge = pci_upstream_bridge(pdev);
+ if (bridge && !pci_dev_trylock(bridge))
+ goto out_restore_state;
+ if (pci_dev_trylock(pdev)) {
+ if (!__pci_reset_function_locked(pdev))
+ vdev->needs_reset = false;
+ pci_dev_unlock(pdev);
+ }
+ if (bridge)
+ pci_dev_unlock(bridge);
}
- if (bridge)
- pci_dev_unlock(bridge);
}
out_restore_state:
@@ -1441,6 +1473,20 @@ static int vfio_pci_ioctl_set_irqs(struct vfio_pci_core_device *vdev,
return ret;
}
+/*
+ * Reset a function the way a guest asked for. A CXL Type-2 device resets
+ * through its DVSEC sequence: the host cxl_reset method would collide
+ * with the exclusive HDM range this driver holds and fail busy. Everything else
+ * takes a standard PCI function reset. The caller holds memory_lock, which the
+ * DVSEC sequence requires.
+ */
+int vfio_pci_reset_function(struct vfio_pci_core_device *vdev)
+{
+ if (vdev->cxl_ops && vdev->cxl_ops->reset)
+ return vdev->cxl_ops->reset(vdev);
+ return pci_try_reset_function(vdev->pdev);
+}
+
static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev,
void __user *arg)
{
@@ -1463,7 +1509,7 @@ static int vfio_pci_ioctl_reset(struct vfio_pci_core_device *vdev,
vfio_pci_set_power_state(vdev, PCI_D0);
vfio_pci_dma_buf_move(vdev, true);
- ret = pci_try_reset_function(vdev->pdev);
+ ret = vfio_pci_reset_function(vdev);
vfio_pci_cxl_post_reset(vdev);
if (__vfio_pci_memory_enabled(vdev))
vfio_pci_dma_buf_move(vdev, false);
@@ -2736,6 +2782,21 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
goto err_unlock;
}
+ /*
+ * A multifunction CXL Type-2 device cannot be bus reset: its DVSEC
+ * reset acts per function, so a shared secondary bus reset would reset
+ * sibling functions out from under their own state. Reject it, matching
+ * the cxl_reset bus method. A single-function device is quiesced through
+ * its DVSEC sequence just before the reset below.
+ */
+ list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list) {
+ if (vdev->cxl_ops && vdev->cxl_ops->reset &&
+ vdev->pdev->multifunction) {
+ ret = -ENOTTY;
+ goto err_unlock;
+ }
+ }
+
/*
* Some of the devices in the dev_set can be in the runtime suspended
* state. Increment the usage count for all the devices in the dev_set
@@ -2817,11 +2878,57 @@ 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)
vfio_pci_set_power_state(vdev, PCI_D0);
+ /*
+ * Quiesce each CXL Type-2 device through its DVSEC sequence before the
+ * secondary bus reset: the bus reset alone does not write back the
+ * device cache or tear down the HDM decoders. memory_lock is held. If a
+ * quiesce fails, abort before the bus reset: resetting an unquiesced CXL
+ * device risks data loss or a fabric error. Every device is locked here,
+ * so unwind from the last one.
+ */
+ list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list) {
+ if (vdev->cxl_ops && vdev->cxl_ops->reset) {
+ ret = vdev->cxl_ops->reset(vdev);
+ if (ret) {
+ pci_warn(vdev->pdev, "vfio-cxl: hot reset: DVSEC quiesce failed (%d), aborting before bus reset\n",
+ ret);
+ vdev = list_last_entry(&dev_set->device_list,
+ struct vfio_pci_core_device,
+ vdev.dev_set_list);
+ goto err_undo;
+ }
+ }
+ }
+
ret = pci_reset_bus(pdev);
- /* Re-sample decoder state for any CXL device the bus reset touched. */
- list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list)
- vfio_pci_cxl_post_reset(vdev);
+ /*
+ * pci_reset_bus() restored each device's PCI_COMMAND, which can re-enable
+ * Bus Master, but the secondary bus reset cleared the physical HDM decoder
+ * that the CXL quiesce above restored. Until it is restored a CXL device
+ * could DMA over cleared decode, so for each CXL device drop Bus Master,
+ * restore and re-sample the decoder, then re-enable Bus Master to the
+ * guest's intent only once the decoder is known good. Re-sampling alone
+ * would leave hdm_valid true over a cleared decoder. Keep the first restore
+ * error so a failed restore is reported instead of the bus reset's success.
+ */
+ list_for_each_entry(vdev, &dev_set->device_list, vdev.dev_set_list) {
+ u16 cmd;
+ int rret;
+
+ if (!(vdev->cxl_ops && vdev->cxl_ops->reset))
+ continue;
+
+ pci_read_config_word(vdev->pdev, PCI_COMMAND, &cmd);
+ pci_clear_master(vdev->pdev);
+ rret = vfio_pci_cxl_pm_restore(vdev);
+ if (rret) {
+ if (!ret)
+ ret = rret;
+ } else if (cmd & PCI_COMMAND_MASTER) {
+ pci_set_master(vdev->pdev);
+ }
+ }
vdev = list_last_entry(&dev_set->device_list,
struct vfio_pci_core_device, vdev.dev_set_list);
@@ -2876,6 +2983,16 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
if (!pdev)
return;
+ /*
+ * A multifunction CXL Type-2 device cannot be bus reset (its DVSEC
+ * reset is per function), so skip the automatic reset rather than reset
+ * sibling functions out from under their state.
+ */
+ list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list)
+ if (cur->cxl_ops && cur->cxl_ops->reset &&
+ cur->pdev->multifunction)
+ return;
+
/*
* Some of the devices in the bus can be in the runtime suspended
* state. Increment the usage count for all the devices in the dev_set
@@ -2884,9 +3001,56 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
if (vfio_pci_dev_set_pm_runtime_get(dev_set))
return;
+ /*
+ * Quiesce each CXL Type-2 device through its DVSEC sequence before the
+ * bus reset, which alone does not write back the device cache or tear
+ * down the HDM decoders. Take memory_lock and zap the HDM window as the
+ * explicit hot reset does. On lock contention or a failed quiesce, skip
+ * the bus reset and leave needs_reset set for a later retry.
+ */
+ list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) {
+ if (!(cur->cxl_ops && cur->cxl_ops->reset))
+ continue;
+ if (!down_write_trylock(&cur->memory_lock))
+ goto unwind;
+ vfio_pci_cxl_zap(cur);
+ if (cur->cxl_ops->reset(cur)) {
+ up_write(&cur->memory_lock);
+ goto unwind;
+ }
+ }
+
if (!pci_reset_bus(pdev))
reset_done = true;
+ list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) {
+ u16 cmd;
+
+ if (!(cur->cxl_ops && cur->cxl_ops->reset))
+ continue;
+ /*
+ * pci_reset_bus() restored PCI_COMMAND, which can re-enable Bus
+ * Master, but the bus reset cleared the physical HDM decoder. Drop
+ * Bus Master, restore and re-sample the decoder, then re-enable Bus
+ * Master to the guest's intent once the decoder is known good.
+ * Re-sampling alone would leave hdm_valid true over a cleared
+ * decoder.
+ */
+ pci_read_config_word(cur->pdev, PCI_COMMAND, &cmd);
+ pci_clear_master(cur->pdev);
+ if (!vfio_pci_cxl_pm_restore(cur) && (cmd & PCI_COMMAND_MASTER))
+ pci_set_master(cur->pdev);
+ up_write(&cur->memory_lock);
+ }
+ goto out;
+
+unwind:
+ list_for_each_entry_continue_reverse(cur, &dev_set->device_list,
+ vdev.dev_set_list)
+ if (cur->cxl_ops && cur->cxl_ops->reset)
+ up_write(&cur->memory_lock);
+
+out:
list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) {
if (reset_done)
cur->needs_reset = false;
@@ -2919,6 +3083,29 @@ void vfio_pci_core_unregister_cxl_ops(const struct vfio_cxl_ops *ops)
}
EXPORT_SYMBOL_GPL(vfio_pci_core_unregister_cxl_ops);
+/*
+ * Drive a guest-requested CXL reset. The memory_lock, mapping revoke and
+ * dma-buf quiesce are core-internal, so vfio-cxl calls in here to run them
+ * around its DVSEC reset sequence.
+ */
+int vfio_pci_core_cxl_reset(struct vfio_pci_core_device *vdev)
+{
+ int ret;
+
+ if (!vdev->cxl_ops || !vdev->cxl_ops->reset)
+ return -ENOTTY;
+
+ vfio_pci_zap_and_down_write_memory_lock(vdev);
+ vfio_pci_dma_buf_move(vdev, true);
+ ret = vdev->cxl_ops->reset(vdev);
+ if (__vfio_pci_memory_enabled(vdev))
+ vfio_pci_dma_buf_move(vdev, false);
+ up_write(&vdev->memory_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_cxl_reset);
+
static void vfio_pci_core_cleanup(void)
{
vfio_pci_uninit_perm_bits();
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index 46e67573d264..8055099cab03 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -102,6 +102,8 @@ static inline int vfio_pci_cxl_pm_restore(struct vfio_pci_core_device *vdev)
return 0;
}
+int vfio_pci_reset_function(struct vfio_pci_core_device *vdev);
+
u16 vfio_pci_memory_lock_and_enable(struct vfio_pci_core_device *vdev);
void vfio_pci_memory_unlock_and_restore(struct vfio_pci_core_device *vdev,
u16 cmd);
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index c438d968dc59..18e206a35d8c 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -82,6 +82,8 @@ struct vfio_cxl_ops {
void (*post_reset)(struct vfio_pci_core_device *vdev);
/* Restore the HDM decoder after a D3hot->D0 soft reset */
int (*pm_restore)(struct vfio_pci_core_device *vdev);
+ /* Run the CXL reset sequence; the core holds memory_lock across it */
+ int (*reset)(struct vfio_pci_core_device *vdev);
/* Pinned per bound CXL device so vfio-cxl cannot unload under usage */
struct module *owner;
@@ -89,6 +91,7 @@ struct vfio_cxl_ops {
int vfio_pci_core_register_cxl_ops(const struct vfio_cxl_ops *ops);
void vfio_pci_core_unregister_cxl_ops(const struct vfio_cxl_ops *ops);
+int vfio_pci_core_cxl_reset(struct vfio_pci_core_device *vdev);
#if IS_ENABLED(CONFIG_VFIO_PCI_DMABUF)
int vfio_pci_core_fill_phys_vec(struct phys_vec *phys_vec,
--
2.25.1
next prev parent reply other threads:[~2026-08-13 9:41 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-13 9:36 [PATCH v4 00/27] vfio/pci: Add CXL Type-2 device passthrough support mhonap
2026-08-13 9:36 ` [PATCH v4 01/27] cxl: Fix resource.c include path and export cxl_restore_hdm_after_pci_reset mhonap
2026-08-21 22:52 ` Jonathan Cameron
2026-08-22 1:22 ` Manish Honap
2026-08-13 9:36 ` [PATCH v4 02/27] cxl/regs: Skip sub-block region request for BAR-owning drivers mhonap
2026-08-13 9:36 ` [PATCH v4 03/27] cxl: Move component register defines to uapi/cxl/cxl_regs.h mhonap
2026-08-13 9:36 ` [PATCH v4 04/27] cxl: Establish media readiness in cxl_mem_probe() mhonap
2026-08-13 9:36 ` [PATCH v4 05/27] cxl: Add a function-scoped reset entry for vfio-pci mhonap
2026-08-13 9:36 ` [PATCH v4 06/27] vfio/pci: Add CXL ops registration interface mhonap
2026-08-13 9:36 ` [PATCH v4 07/27] vfio/pci: Detect CXL devices and load vfio-cxl on demand mhonap
2026-08-13 9:36 ` [PATCH v4 08/27] vfio/cxl: Add the vfio-cxl module skeleton mhonap
2026-08-13 9:36 ` [PATCH v4 09/27] vfio/cxl: Create the CXL memory device at bind mhonap
2026-08-13 9:36 ` [PATCH v4 10/27] vfio/cxl: Reject unsupported decoder topologies " mhonap
2026-08-13 9:36 ` [PATCH v4 11/27] vfio/cxl: Own the whole component register BAR mhonap
2026-08-13 9:36 ` [PATCH v4 12/27] vfio/pci: Let a provider exclude a BAR sub-range from mmap mhonap
2026-08-13 9:36 ` [PATCH v4 13/27] vfio/pci: Refuse read/write to an excluded BAR sub-range mhonap
2026-08-13 9:36 ` [PATCH v4 14/27] vfio: Add CXL region type for the HDM region mhonap
2026-08-13 9:36 ` [PATCH v4 15/27] vfio/pci: Call CXL open and close hooks around device use mhonap
2026-08-13 9:36 ` [PATCH v4 16/27] vfio/cxl: Shadow the CXL DVSEC body at open mhonap
2026-08-13 9:36 ` [PATCH v4 17/27] vfio/cxl: Virtualize the CXL DVSEC mhonap
2026-08-13 9:36 ` [PATCH v4 18/27] vfio/cxl: Expose the HDM memory and trap the decoder registers mhonap
2026-08-13 9:36 ` [PATCH v4 19/27] vfio/cxl: Keep the HDM decoder block off the direct BAR mapping mhonap
2026-08-13 9:36 ` [PATCH v4 20/27] vfio/cxl: Emulate the HDM decoder commit handshake mhonap
2026-08-13 9:36 ` [PATCH v4 21/27] vfio/cxl: Describe the CXL device and decoder geometry to userspace mhonap
2026-08-13 9:36 ` [PATCH v4 22/27] vfio/cxl: Revoke the HDM mapping on reset and power transitions mhonap
2026-08-13 9:36 ` [PATCH v4 23/27] vfio/cxl: Refresh the decoder snapshot after a device reset mhonap
2026-08-13 9:36 ` mhonap [this message]
2026-08-13 9:36 ` [PATCH v4 25/27] vfio/pci: Provide an opt-out for the CXL Type-2 extensions mhonap
2026-08-13 9:36 ` [PATCH v4 26/27] Documentation: vfio-pci: Document CXL Type-2 device passthrough mhonap
2026-08-13 9:36 ` [PATCH v4 27/27] selftests/vfio: Add CXL Type-2 passthrough corner-case tests mhonap
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260813093631.2288172-25-mhonap@nvidia.com \
--to=mhonap@nvidia.com \
--cc=alejandro.lucero-palau@amd.com \
--cc=alex@shazbot.org \
--cc=alison.schofield@intel.com \
--cc=ankita@nvidia.com \
--cc=bhelgaas@google.com \
--cc=cjia@nvidia.com \
--cc=corbet@lwn.net \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=dmatlack@google.com \
--cc=gustavoars@kernel.org \
--cc=iweiny@kernel.org \
--cc=jgg@ziepe.ca \
--cc=jic23@kernel.org \
--cc=kees@kernel.org \
--cc=kevin.tian@intel.com \
--cc=kjaju@nvidia.com \
--cc=kvm@vger.kernel.org \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=ming.li@zohomail.com \
--cc=skhan@linuxfoundation.org \
--cc=skolothumtho@nvidia.com \
--cc=smadhavan@nvidia.com \
--cc=vishal.l.verma@intel.com \
--cc=vsethi@nvidia.com \
--cc=yishaih@nvidia.com \
--cc=zhiw@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.