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: 38+ 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-25 21:26 ` Alex Williamson
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-25 22:18 ` Alex Williamson
2026-08-13 9:36 ` [PATCH v4 05/27] cxl: Add a function-scoped reset entry for vfio-pci mhonap
2026-08-25 23:11 ` Alex Williamson
2026-08-13 9:36 ` [PATCH v4 06/27] vfio/pci: Add CXL ops registration interface mhonap
2026-08-26 21:11 ` Alex Williamson
2026-08-13 9:36 ` [PATCH v4 07/27] vfio/pci: Detect CXL devices and load vfio-cxl on demand mhonap
2026-08-26 22:17 ` Alex Williamson
2026-08-13 9:36 ` [PATCH v4 08/27] vfio/cxl: Add the vfio-cxl module skeleton mhonap
2026-08-26 22:50 ` Alex Williamson
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
2026-08-26 7:28 ` Shuai Xue
2026-08-26 16:17 ` Manish Honap
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox