mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v8 0/4] vfio-pci/zdev: Improved zPCI Function Measurement Support
@ 2026-10-05 15:45 Omar Elghoul
  2026-10-05 15:45 ` [PATCH v8 1/4] s390/pci: Hold fmb_lock when enabling or disabling PCI devices Omar Elghoul
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Omar Elghoul @ 2026-10-05 15:45 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: oelghoul, hca, gor, agordeev, borntraeger, svens, schnelle,
	mjrosato, alifm, farman, gbayer, pasic, alex, frankja, imbrenda

Hi,

This patch series improves support for function measurement for zPCI
passthrough devices on s390.

Changelog
=========
v7 -> v8:
* Patch 2/4:
  - Replace the fmb_enabled bitfield in struct zpci_dev with a dedicated
    bool to avoid future tearing bugs
  - Rewrite commit message for a more natural flow

* Patch 4/4:
  - Replace mutex_lock/unlock() in vfio_pci_zdev_feature_fmb_read() with
    a scoped_guard()

v6 -> v7:
* Patch 2/4:
  - Don't re-enable FMB if it wasn't already enabled

v5 -> v6:
* Patch 2/4:
  - Rework the FMB re-enablement code to reuse the same buffer again
  - Make the FMB buffer persistent once allocated for as long as the
    device's lifetime to accommodate an architectural quirk
* Patch 4/4:
  - Update the liveness check to use the new FMB enabled bool

v4 -> v5:
* Typo in the cover letter
* Swap the ordering of patches 3/4 and 4/4 to ease merging (i.e., to
  ensure the three s390 patches are ordered before the VFIO patch)
* Patch 2/4:
  - Drop the refactor of zpci_fmb_enable_device() and the separation of
    zpci_fmb_clear_iommu_ctrs() and zpci_fmb_do_enable()
  - Allocate a new buffer in zpci_fmb_reenable_device() rather than
    reusing the same buffer to avoid firmware edge cases

* Patch 3/4 (previously 4/4):
  - Avoid reading from userspace while holding kzdev_lock unnecessarily

* Patch 4/4 (previously 3/4):
  - Drop allowing usercopy of the FMB when initializing the kmem_cache
  - Avoid copying to userspace while holding fmb_lock unnecessarily
    - Restore the FMB bounce buffer to achieve this one
  - Clarify uAPI documentation and ensure it accurately describes the
    behavior of the VFIO features

v3 -> v4:
* Patch 2/4:
  - Replace mutex_lock/unlock in zpci_reenable_device() with a guard

* Patch 3/4:
  - Allow usercopy of the FMB when initializing its kmem_cache
  - Move the guard in vfio_pci_zdev_feature_fmb_enable() lower to only
    protect the FMB
  - Ensure vfio_pci_zdev_feature_fmb_enable() fails on double-enable for
    consistency with the documentation
  - Eliminate the bounce buffer in vfio_pci_zdev_feature_fmb_read()
  - Replace the void pointer with __aligned_u64 in the FMB read uAPI
    structure

v2 -> v3:
* Patch 1/4 (new patch):
  - Fix race conditions in pcibios_enable/disable_device() with regard to
    the FMB enable/disable
  - Assert that fmb_lock is held within zpci_fmb_enable_device() and
    zpci_fmb_disable_device()

* Patch 2/4 (previously 1/3):
  - Move the FMB enable logic into a static function zpci_fmb_do_enable()
    to reduce code duplication between zpci_fmb_enable_device() and
    zpci_fmb_reenable_device()
  - Reword commit message to use the imperative voice more consistently

* Patch 3/4 (previously 2/3):
  - Split the previous VFIO feature into a SET-only and a GET-only feature
    for enabling/disabling and reading the FMB respectively
  - Remove FMB definitions from the VFIO uAPI and instead treat it as an
    opaque structure

* Patch 4/4 (previously 3/3):
  - Clarify goto label name to reduce misunderstandings

v1 -> v2:
* Patch 1/3:
  - Address a possible race condition in zpci_reenable_device() caused by
    calling zpci_fmb_reenable_device() without holding fmb_lock
  - Assert that fmb_lock is held within zpci_fmb_reenable_device()

* Patch 3/3:
  - Address a possible race condition in pci_perf_seq_write() caused by
    consuming zdev->kzdev without holding kzdev_lock

Motivation
==========
The firmware on s390x machines allows for tracking a variety of statistics
relating to zPCI devices in a function measurement block (FMB). However,
the kernel currently lacks a structured mechanism of sharing this
information with userspace, beyond /sys/kernel/debug/pci/ID/statistics.
This can lead to shortcomings when running a guest on KVM with PCI
passthrough devices, as QEMU is unable to provide an accurate FMB snapshot
to the guest.

Proposal
========
We propose adding a new VFIO device feature to zPCI passthrough devices,
allowing userspace programs to read the latest FMB snapshot as it is
written by the firmware. We ensure that function measurement enablement is
preserved across device resets on the host. Furthermore, we guard against
host tampering with the FMB via sysfs when the zPCI device is in
passthrough to protect the VM's state.

I'd appreciate some feedback on these patches.

Thanks in advance.

Omar Elghoul (4):
  s390/pci: Hold fmb_lock when enabling or disabling PCI devices
  s390/pci: Reuse FMB buffer and preserve state in device re-enablement
  s390/pci: Fence FMB enable/disable via debugfs for passthrough devices
  vfio-pci/zdev: Add VFIO FMB device features

 arch/s390/include/asm/pci.h      |  2 +
 arch/s390/pci/pci.c              | 84 +++++++++++++++++++++++++-------
 arch/s390/pci/pci_debug.c        | 11 ++++-
 drivers/vfio/pci/vfio_pci_core.c |  4 ++
 drivers/vfio/pci/vfio_pci_priv.h | 18 +++++++
 drivers/vfio/pci/vfio_pci_zdev.c | 57 ++++++++++++++++++++++
 include/uapi/linux/vfio.h        | 29 +++++++++++
 7 files changed, 187 insertions(+), 18 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v8 1/4] s390/pci: Hold fmb_lock when enabling or disabling PCI devices
  2026-10-05 15:45 [PATCH v8 0/4] vfio-pci/zdev: Improved zPCI Function Measurement Support Omar Elghoul
@ 2026-10-05 15:45 ` Omar Elghoul
  2026-10-05 15:45 ` [PATCH v8 2/4] s390/pci: Reuse FMB buffer and preserve state in device re-enablement Omar Elghoul
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Omar Elghoul @ 2026-10-05 15:45 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: oelghoul, hca, gor, agordeev, borntraeger, svens, schnelle,
	mjrosato, alifm, farman, gbayer, pasic, alex, frankja, imbrenda,
	stable

Ensure that fmb_lock is held by pcibios_enable_device() and
pcibios_disable_device() when calling zpci_fmb_enable_device() or
zpci_fmb_disable_device(), respectively. Additionally, assert that the
fmb_lock is held within the latter two functions to prevent future race
conditions regarding new callers.

Fixes: af0a8a8453f7 ("s390/pci: implement pcibios_add_device")
Fixes: 944239c59e93 ("s390/pci: implement pcibios_release_device")
Cc: stable@vger.kernel.org
Signed-off-by: Omar Elghoul <oelghoul@linux.ibm.com>
Reviewed-by: Niklas Schnelle <schnelle@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
---
 arch/s390/pci/pci.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index d64c544b32b0..c055a9ad0972 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -173,6 +173,8 @@ int zpci_fmb_enable_device(struct zpci_dev *zdev)
 	unsigned long flags;
 	u8 cc, status;
 
+	lockdep_assert_held(&zdev->fmb_lock);
+
 	if (zdev->fmb || sizeof(*zdev->fmb) < zdev->fmb_length)
 		return -EINVAL;
 
@@ -211,6 +213,8 @@ int zpci_fmb_disable_device(struct zpci_dev *zdev)
 	struct zpci_fib fib = {0};
 	u8 cc, status;
 
+	lockdep_assert_held(&zdev->fmb_lock);
+
 	if (!zdev->fmb)
 		return -EINVAL;
 
@@ -639,7 +643,9 @@ int pcibios_enable_device(struct pci_dev *pdev, int mask)
 	struct zpci_dev *zdev = to_zpci(pdev);
 
 	zpci_debug_init_device(zdev, dev_name(&pdev->dev));
+	mutex_lock(&zdev->fmb_lock);
 	zpci_fmb_enable_device(zdev);
+	mutex_unlock(&zdev->fmb_lock);
 
 	return pci_enable_resources(pdev, mask);
 }
@@ -648,7 +654,9 @@ void pcibios_disable_device(struct pci_dev *pdev)
 {
 	struct zpci_dev *zdev = to_zpci(pdev);
 
+	mutex_lock(&zdev->fmb_lock);
 	zpci_fmb_disable_device(zdev);
+	mutex_unlock(&zdev->fmb_lock);
 	zpci_debug_exit_device(zdev);
 }
 
-- 
2.55.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v8 2/4] s390/pci: Reuse FMB buffer and preserve state in device re-enablement
  2026-10-05 15:45 [PATCH v8 0/4] vfio-pci/zdev: Improved zPCI Function Measurement Support Omar Elghoul
  2026-10-05 15:45 ` [PATCH v8 1/4] s390/pci: Hold fmb_lock when enabling or disabling PCI devices Omar Elghoul
@ 2026-10-05 15:45 ` Omar Elghoul
  2026-10-05 19:53   ` Niklas Schnelle
  2026-10-05 15:45 ` [PATCH v8 3/4] s390/pci: Fence FMB enable/disable via debugfs for passthrough devices Omar Elghoul
  2026-10-05 15:45 ` [PATCH v8 4/4] vfio-pci/zdev: Add VFIO FMB device features Omar Elghoul
  3 siblings, 1 reply; 8+ messages in thread
From: Omar Elghoul @ 2026-10-05 15:45 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: oelghoul, hca, gor, agordeev, borntraeger, svens, schnelle,
	mjrosato, alifm, farman, gbayer, pasic, alex, frankja, imbrenda

Don't free the FMB buffer when disabling measurement in
zpci_fmb_disable_device(). Instead, make the buffer persistent for the
lifetime of the device and reuse it across enable/disable cycles. Defer
freeing the buffer until teardown in zpci_release_device().

To support the persistent buffers, add the fmb_enabled bool to struct
zpci_dev to decouple whether FMB is enabled from whether the buffer has
been allocated. Audit the only consumer of zdev->fmb as a liveness check
and update it to reflect this change.

Introduce the function zpci_fmb_reenable_device() to ensure that the FMB
is enabled. If it was already enabled, disable it, zero the counters,
and re-enable it. This allows the function to be used in both first-time
enabling and re-enabling measurement. Call it in zpci_reenable_device()
to preserve the FMB enablement if it had been implicitly disabled by
firmware in zpci_disable_device().

Signed-off-by: Omar Elghoul <oelghoul@linux.ibm.com>
---
 arch/s390/include/asm/pci.h |  2 +
 arch/s390/pci/pci.c         | 76 ++++++++++++++++++++++++++++---------
 arch/s390/pci/pci_debug.c   |  2 +-
 3 files changed, 62 insertions(+), 18 deletions(-)

diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
index 88a125b92bdd..2920de1f9484 100644
--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -205,6 +205,7 @@ struct zpci_dev {
 
 	/* Function measurement block */
 	struct mutex fmb_lock;
+	bool fmb_enabled;
 	struct zpci_fmb *fmb;
 	u16		fmb_update;	/* update interval */
 	u16		fmb_length;
@@ -351,6 +352,7 @@ void zpci_remove_parent_msi_domain(struct zpci_bus *zbus);
 /* FMB */
 int zpci_fmb_enable_device(struct zpci_dev *);
 int zpci_fmb_disable_device(struct zpci_dev *);
+int zpci_fmb_reenable_device(struct zpci_dev *zdev);
 
 /* Debug */
 int zpci_debug_init(void);
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index c055a9ad0972..ec729be91f97 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -175,13 +175,17 @@ int zpci_fmb_enable_device(struct zpci_dev *zdev)
 
 	lockdep_assert_held(&zdev->fmb_lock);
 
-	if (zdev->fmb || sizeof(*zdev->fmb) < zdev->fmb_length)
+	if (zdev->fmb_enabled || sizeof(*zdev->fmb) < zdev->fmb_length)
 		return -EINVAL;
 
-	zdev->fmb = kmem_cache_zalloc(zdev_fmb_cache, GFP_KERNEL);
-	if (!zdev->fmb)
-		return -ENOMEM;
-	WARN_ON((u64) zdev->fmb & 0xf);
+	if (!zdev->fmb) {
+		zdev->fmb = kmem_cache_zalloc(zdev_fmb_cache, GFP_KERNEL);
+		if (!zdev->fmb)
+			return -ENOMEM;
+	} else {
+		/* reuse the same FMB buffer for as long the zdev lives */
+		memset(zdev->fmb, 0, sizeof(*zdev->fmb));
+	}
 
 	/* reset software counters */
 	spin_lock_irqsave(&zdev->dom_lock, flags);
@@ -199,11 +203,11 @@ int zpci_fmb_enable_device(struct zpci_dev *zdev)
 	fib.fmb_addr = virt_to_phys(zdev->fmb);
 	fib.gd = zdev->gisa;
 	cc = zpci_mod_fc(req, &fib, &status);
-	if (cc) {
-		kmem_cache_free(zdev_fmb_cache, zdev->fmb);
-		zdev->fmb = NULL;
-	}
-	return cc ? -EIO : 0;
+	if (cc)
+		return -EIO;
+
+	zdev->fmb_enabled = true;
+	return 0;
 }
 
 /* Modify PCI: Disable PCI function measurement */
@@ -215,7 +219,7 @@ int zpci_fmb_disable_device(struct zpci_dev *zdev)
 
 	lockdep_assert_held(&zdev->fmb_lock);
 
-	if (!zdev->fmb)
+	if (!zdev->fmb_enabled)
 		return -EINVAL;
 
 	fib.gd = zdev->gisa;
@@ -224,13 +228,39 @@ int zpci_fmb_disable_device(struct zpci_dev *zdev)
 	cc = zpci_mod_fc(req, &fib, &status);
 	if (cc == 3) /* Function already gone. */
 		cc = 0;
+	if (cc)
+		return -EIO;
 
-	if (!cc) {
-		kmem_cache_free(zdev_fmb_cache, zdev->fmb);
-		zdev->fmb = NULL;
-	}
-	return cc ? -EIO : 0;
+	zdev->fmb_enabled = false;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zpci_fmb_disable_device);
+
+int zpci_fmb_reenable_device(struct zpci_dev *zdev)
+{
+	u64 req = ZPCI_CREATE_REQ(zdev->fh, 0, ZPCI_MOD_FC_SET_MEASURE);
+	struct zpci_fib fib = {0};
+	u8 cc, status;
+
+	lockdep_assert_held(&zdev->fmb_lock);
+
+	if (!zdev->fmb_enabled)
+		return zpci_fmb_enable_device(zdev);
+
+	fib.gd = zdev->gisa;
+	cc = zpci_mod_fc(req, &fib, &status); /* Disable function measurement */
+
+	/* Unlike in zpci_fmb_disable_device(), cc == 3 is not a valid state here
+	 * because we are re-enabling function measurement for the same function
+	 * handle.
+	 */
+	if (cc)
+		return -EIO;
+
+	zdev->fmb_enabled = false;
+	return zpci_fmb_enable_device(zdev);
 }
+EXPORT_SYMBOL_GPL(zpci_fmb_reenable_device);
 
 static int zpci_cfg_load(struct zpci_dev *zdev, int offset, u32 *val, u8 len)
 {
@@ -737,8 +767,14 @@ int zpci_reenable_device(struct zpci_dev *zdev)
 	}
 
 	rc = zpci_iommu_register_ioat(zdev, &status);
-	if (rc)
+	if (rc) {
 		zpci_disable_device(zdev);
+		return rc;
+	}
+
+	guard(mutex)(&zdev->fmb_lock);
+	if (zdev->fmb_enabled)
+		zpci_fmb_reenable_device(zdev);
 
 	return rc;
 }
@@ -1003,6 +1039,12 @@ void zpci_release_device(struct kref *kref)
 	if (zdev->has_resources)
 		zpci_cleanup_bus_resources(zdev);
 
+	if (zdev->fmb) {
+		zdev->fmb_enabled = false;
+		kmem_cache_free(zdev_fmb_cache, zdev->fmb);
+		zdev->fmb = NULL;
+	}
+
 	zpci_bus_device_unregister(zdev);
 	zpci_destroy_iommu(zdev);
 	zpci_dbg(3, "rem fid:%x\n", zdev->fid);
diff --git a/arch/s390/pci/pci_debug.c b/arch/s390/pci/pci_debug.c
index c7ed7bf254b5..44f026ead414 100644
--- a/arch/s390/pci/pci_debug.c
+++ b/arch/s390/pci/pci_debug.c
@@ -97,7 +97,7 @@ static int pci_perf_show(struct seq_file *m, void *v)
 		return 0;
 
 	mutex_lock(&zdev->fmb_lock);
-	if (!zdev->fmb) {
+	if (!zdev->fmb_enabled) {
 		mutex_unlock(&zdev->fmb_lock);
 		seq_puts(m, "FMB statistics disabled\n");
 		return 0;
-- 
2.55.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v8 3/4] s390/pci: Fence FMB enable/disable via debugfs for passthrough devices
  2026-10-05 15:45 [PATCH v8 0/4] vfio-pci/zdev: Improved zPCI Function Measurement Support Omar Elghoul
  2026-10-05 15:45 ` [PATCH v8 1/4] s390/pci: Hold fmb_lock when enabling or disabling PCI devices Omar Elghoul
  2026-10-05 15:45 ` [PATCH v8 2/4] s390/pci: Reuse FMB buffer and preserve state in device re-enablement Omar Elghoul
@ 2026-10-05 15:45 ` Omar Elghoul
  2026-10-05 18:42   ` Niklas Schnelle
  2026-10-05 15:45 ` [PATCH v8 4/4] vfio-pci/zdev: Add VFIO FMB device features Omar Elghoul
  3 siblings, 1 reply; 8+ messages in thread
From: Omar Elghoul @ 2026-10-05 15:45 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: oelghoul, hca, gor, agordeev, borntraeger, svens, schnelle,
	mjrosato, alifm, farman, gbayer, pasic, alex, frankja, imbrenda

Introduce a fence over enabling or disabling FMB via debugfs when the zPCI
device is associated with a KVM. This will prevent processes on the host
from tampering with the FMB while the guest is still using it, which may
cause partial counter resets and inconsistent reads which have no parallel
in the architecture.

For VFIO devices that are not associated with a KVM (i.e., for userspace
drivers other than QEMU), this fence does not take effect.

Signed-off-by: Omar Elghoul <oelghoul@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
---
 arch/s390/pci/pci_debug.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/s390/pci/pci_debug.c b/arch/s390/pci/pci_debug.c
index 44f026ead414..afc52bd5271a 100644
--- a/arch/s390/pci/pci_debug.c
+++ b/arch/s390/pci/pci_debug.c
@@ -153,6 +153,12 @@ static ssize_t pci_perf_seq_write(struct file *file, const char __user *ubuf,
 	if (rc)
 		return rc;
 
+	mutex_lock(&zdev->kzdev_lock);
+	if (zdev->kzdev) {
+		rc = -EPERM;
+		goto out_unlock_kzdev;
+	}
+
 	mutex_lock(&zdev->fmb_lock);
 	switch (val) {
 	case 0:
@@ -163,6 +169,9 @@ static ssize_t pci_perf_seq_write(struct file *file, const char __user *ubuf,
 		break;
 	}
 	mutex_unlock(&zdev->fmb_lock);
+
+out_unlock_kzdev:
+	mutex_unlock(&zdev->kzdev_lock);
 	return rc ? rc : count;
 }
 
-- 
2.55.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v8 4/4] vfio-pci/zdev: Add VFIO FMB device features
  2026-10-05 15:45 [PATCH v8 0/4] vfio-pci/zdev: Improved zPCI Function Measurement Support Omar Elghoul
                   ` (2 preceding siblings ...)
  2026-10-05 15:45 ` [PATCH v8 3/4] s390/pci: Fence FMB enable/disable via debugfs for passthrough devices Omar Elghoul
@ 2026-10-05 15:45 ` Omar Elghoul
  3 siblings, 0 replies; 8+ messages in thread
From: Omar Elghoul @ 2026-10-05 15:45 UTC (permalink / raw)
  To: linux-s390, linux-kernel, kvm
  Cc: oelghoul, hca, gor, agordeev, borntraeger, svens, schnelle,
	mjrosato, alifm, farman, gbayer, pasic, alex, frankja, imbrenda

Introduce new VFIO features for zPCI devices to provide FMB passthrough to
userspace.

Allow the user to enable or disable the FMB using the SET-only feature
VFIO_DEVICE_FEATURE_ZPCI_FMB_ENABLE. Likewise allow the user to read the
latest FMB using the GET-only feature VFIO_DEVICE_FEATURE_ZPCI_FMB_READ
in the case when the FMB is enabled.

Signed-off-by: Omar Elghoul <oelghoul@linux.ibm.com>
---
 drivers/vfio/pci/vfio_pci_core.c |  4 +++
 drivers/vfio/pci/vfio_pci_priv.h | 18 ++++++++++
 drivers/vfio/pci/vfio_pci_zdev.c | 57 ++++++++++++++++++++++++++++++++
 include/uapi/linux/vfio.h        | 29 ++++++++++++++++
 4 files changed, 108 insertions(+)

diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 6757054e9d87..3c827a77725b 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -1627,6 +1627,10 @@ int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,
 		return vfio_pci_core_feature_dma_buf(vdev, flags, arg, argsz);
 	case VFIO_DEVICE_FEATURE_ZPCI_ERROR:
 		return vfio_pci_zdev_feature_err(device, flags, arg, argsz);
+	case VFIO_DEVICE_FEATURE_ZPCI_FMB_ENABLE:
+		return vfio_pci_zdev_feature_fmb_enable(vdev, flags, arg, argsz);
+	case VFIO_DEVICE_FEATURE_ZPCI_FMB_READ:
+		return vfio_pci_zdev_feature_fmb_read(vdev, flags, arg, argsz);
 	default:
 		return -ENOTTY;
 	}
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index 4e7162234a2e..e04d7e9d0c30 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -95,6 +95,10 @@ int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev);
 void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev);
 int vfio_pci_zdev_feature_err(struct vfio_device *device, u32 flags,
 			      void __user *arg, size_t argsz);
+int vfio_pci_zdev_feature_fmb_enable(struct vfio_pci_core_device *vdev, u32 flags,
+				     void __user *arg, size_t argsz);
+int vfio_pci_zdev_feature_fmb_read(struct vfio_pci_core_device *vdev, u32 flags,
+				   void __user *arg, size_t argsz);
 #else
 static inline int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev,
 					      struct vfio_info_cap *caps)
@@ -116,6 +120,20 @@ static inline int vfio_pci_zdev_feature_err(struct vfio_device *device,
 {
 	return -ENOTTY;
 }
+
+static inline int vfio_pci_zdev_feature_fmb_enable(struct vfio_pci_core_device *vdev,
+						   u32 flags, void __user *arg,
+						   size_t argsz)
+{
+	return -ENOTTY;
+}
+
+static inline int vfio_pci_zdev_feature_fmb_read(struct vfio_pci_core_device *vdev,
+						 u32 flags, void __user *arg,
+						 size_t argsz)
+{
+	return -ENOTTY;
+}
 #endif
 
 static inline bool vfio_pci_is_vga(struct pci_dev *pdev)
diff --git a/drivers/vfio/pci/vfio_pci_zdev.c b/drivers/vfio/pci/vfio_pci_zdev.c
index f47f36314a1c..acf4bbcb53f6 100644
--- a/drivers/vfio/pci/vfio_pci_zdev.c
+++ b/drivers/vfio/pci/vfio_pci_zdev.c
@@ -219,3 +219,60 @@ void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev)
 	if (zpci_kvm_hook.kvm_unregister)
 		zpci_kvm_hook.kvm_unregister(zdev);
 }
+
+int vfio_pci_zdev_feature_fmb_enable(struct vfio_pci_core_device *vdev, u32 flags,
+				     void __user *arg, size_t argsz)
+{
+	struct zpci_dev *zdev;
+	struct vfio_device_feature_zpci_fmb_enable fmb_enable;
+	int ret;
+
+	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET, sizeof(fmb_enable));
+	if (ret != 1)
+		return ret;
+
+	zdev = to_zpci(vdev->pdev);
+	if (!zdev)
+		return -ENODEV;
+
+	if (copy_from_user(&fmb_enable, arg, sizeof(fmb_enable)))
+		return -EFAULT;
+
+	guard(mutex)(&zdev->fmb_lock);
+
+	if (fmb_enable.enabled)
+		return zpci_fmb_reenable_device(zdev);
+	return zpci_fmb_disable_device(zdev);
+}
+
+int vfio_pci_zdev_feature_fmb_read(struct vfio_pci_core_device *vdev, u32 flags,
+				   void __user *arg, size_t argsz)
+{
+	struct zpci_dev *zdev;
+	struct vfio_device_feature_zpci_fmb_read fmb_read;
+	struct zpci_fmb fmb_bounce;
+	int ret;
+
+	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET, sizeof(fmb_read));
+	if (ret != 1)
+		return ret;
+
+	zdev = to_zpci(vdev->pdev);
+	if (!zdev)
+		return -ENODEV;
+
+	if (copy_from_user(&fmb_read, arg, sizeof(fmb_read)))
+		return -EFAULT;
+	if (!fmb_read.data)
+		return -EINVAL;
+
+	scoped_guard(mutex, &zdev->fmb_lock) {
+		if (!zdev->fmb_enabled)
+			return -ENOMSG;
+		memcpy(&fmb_bounce, zdev->fmb, zdev->fmb_length);
+	}
+
+	if (copy_to_user(u64_to_user_ptr(fmb_read.data), &fmb_bounce, zdev->fmb_length))
+		return -EFAULT;
+	return 0;
+}
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index e41437fa17ad..a8d501e42ef9 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1555,6 +1555,35 @@ struct vfio_device_feature_zpci_err {
 
 #define VFIO_DEVICE_FEATURE_ZPCI_ERROR 13
 
+/**
+ * Upon VFIO_DEVICE_FEATURE_SET, enable or disable FMB for the VFIO zPCI device.
+ *
+ * enabled is treated as a bool, so any non-zero value evaluates to true. Nested
+ * enabling resets the FMB counters to zero, and nested disabling fails.
+ *
+ * Returns: 0 on success, -1 and errno set appropriately on error.
+ */
+#define VFIO_DEVICE_FEATURE_ZPCI_FMB_ENABLE 14
+
+struct vfio_device_feature_zpci_fmb_enable {
+	__u8 enabled;
+};
+
+/**
+ * Upon VFIO_DEVICE_FEATURE_GET, provide FMB passthrough for VFIO zPCI devices.
+ *
+ * The user-provided buffer must be at least fmb_length large, where fmb_length
+ * is reported in VFIO_DEVICE_INFO_CAP_ZPCI_BASE.
+ *
+ * Returns: 0 on success, -1 and errno set appropriately on error. errno==ENOMSG
+ * when the FMB is not enabled.
+ */
+#define VFIO_DEVICE_FEATURE_ZPCI_FMB_READ 15
+
+struct vfio_device_feature_zpci_fmb_read {
+	__aligned_u64 data;
+};
+
 /* -------- API for Type1 VFIO IOMMU -------- */
 
 /**
-- 
2.55.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v8 3/4] s390/pci: Fence FMB enable/disable via debugfs for passthrough devices
  2026-10-05 15:45 ` [PATCH v8 3/4] s390/pci: Fence FMB enable/disable via debugfs for passthrough devices Omar Elghoul
@ 2026-10-05 18:42   ` Niklas Schnelle
  0 siblings, 0 replies; 8+ messages in thread
From: Niklas Schnelle @ 2026-10-05 18:42 UTC (permalink / raw)
  To: Omar Elghoul, linux-s390, linux-kernel, kvm
  Cc: hca, gor, agordeev, borntraeger, svens, mjrosato, alifm, farman,
	gbayer, pasic, alex, frankja, imbrenda

On Mon, 2026-10-05 at 11:45 -0400, Omar Elghoul wrote:
> Introduce a fence over enabling or disabling FMB via debugfs when the zPCI
> device is associated with a KVM. This will prevent processes on the host
> from tampering with the FMB while the guest is still using it, which may
> cause partial counter resets and inconsistent reads which have no parallel
> in the architecture.
> 
> For VFIO devices that are not associated with a KVM (i.e., for userspace
> drivers other than QEMU), this fence does not take effect.
> 
> Signed-off-by: Omar Elghoul <oelghoul@linux.ibm.com>
> Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
> ---
>  arch/s390/pci/pci_debug.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/arch/s390/pci/pci_debug.c b/arch/s390/pci/pci_debug.c
> index 44f026ead414..afc52bd5271a 100644
> --- a/arch/s390/pci/pci_debug.c
> +++ b/arch/s390/pci/pci_debug.c
> @@ -153,6 +153,12 @@ static ssize_t pci_perf_seq_write(struct file *file, const char __user *ubuf,
>  	if (rc)
>  		return rc;
>  
> +	mutex_lock(&zdev->kzdev_lock);
> +	if (zdev->kzdev) {
> +		rc = -EPERM;
> +		goto out_unlock_kzdev;
> +	}
> +
>  	mutex_lock(&zdev->fmb_lock);
>  	switch (val) {
>  	case 0:
> @@ -163,6 +169,9 @@ static ssize_t pci_perf_seq_write(struct file *file, const char __user *ubuf,
>  		break;
>  	}
>  	mutex_unlock(&zdev->fmb_lock);
> +
> +out_unlock_kzdev:
> +	mutex_unlock(&zdev->kzdev_lock);
>  	return rc ? rc : count;
>  }
>  

Looks good, thank!

Reviewed-by: Niklas Schnelle <schnelle@linux.ibm.com>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v8 2/4] s390/pci: Reuse FMB buffer and preserve state in device re-enablement
  2026-10-05 15:45 ` [PATCH v8 2/4] s390/pci: Reuse FMB buffer and preserve state in device re-enablement Omar Elghoul
@ 2026-10-05 19:53   ` Niklas Schnelle
  2026-10-05 22:05     ` Omar Elghoul
  0 siblings, 1 reply; 8+ messages in thread
From: Niklas Schnelle @ 2026-10-05 19:53 UTC (permalink / raw)
  To: Omar Elghoul, linux-s390, linux-kernel, kvm
  Cc: hca, gor, agordeev, borntraeger, svens, mjrosato, alifm, farman,
	gbayer, pasic, alex, frankja, imbrenda

On Mon, 2026-10-05 at 11:45 -0400, Omar Elghoul wrote:
> Don't free the FMB buffer when disabling measurement in
> zpci_fmb_disable_device(). Instead, make the buffer persistent for the
> lifetime of the device and reuse it across enable/disable cycles. Defer
> freeing the buffer until teardown in zpci_release_device().
> 
> To support the persistent buffers, add the fmb_enabled bool to struct
> zpci_dev to decouple whether FMB is enabled from whether the buffer has
> been allocated. Audit the only consumer of zdev->fmb as a liveness check
> and update it to reflect this change.
> 
> Introduce the function zpci_fmb_reenable_device() to ensure that the FMB
> is enabled. If it was already enabled, disable it, zero the counters,
> and re-enable it. This allows the function to be used in both first-time
> enabling and re-enabling measurement. Call it in zpci_reenable_device()
> to preserve the FMB enablement if it had been implicitly disabled by
> firmware in zpci_disable_device().

I think this causes a sequencing error in zpci_hot_reset_device().
First the device gets disabled via zpci_disable_device(). This
implicitly disables the FMB but keeps zdev->fmb_enabled set. Then we
call zpci_fmb_reenable_device() in zpci_reenable_device(). Since zdev-
>fmb_enabled is set we don't first enable the FMB and instead go
directly to disabling it but that is wrong since the FMB is already
disabled as a side effect of the CLP Set PCI Function (Disable) in
zpci_disable_device().

Also, and I think Gerd mentioned this before, there is a disconnect in
semantics between zpci_fmb_reenable_device() and zpci_reenable_device()
that is quite confusing. While zpci_reenable_device() re-enables the
device with existing interrupts and I/O address translations, after it
was disabled, zpci_fmb_reenable_device() on the other hand does a
disable and then enable cycle.

I think the idea here is that zdev->fmb_enabled tries to track whether
the FMB is supposed to be enabled rather than if it is enabled.  This
makes some sense since the FMB can get disabled by the device entering
the error state or a zpci_disable_device() and we want to know if we
need to re-enable it at the re-enable of the device.

Importantly, unlike the disablement of a device we always initiate the
enablement. But then we can't try to disable the FMB without knowing if
it was already disabled. I think a possible solution for this would be
to have zpci_fmb_reenable_device() mean that we know that the FMB is
disabled but should be enabled, which we know when we re-enable the
device and zdev->fmb_enabled is set. Of course then it doesn't do a
disable but only an enable despite zdev->fmb_enabled already being set,
Then zpci_fmb_enable_device() on the other hand sets the flag initially
and then uses zpci_fmb_reenable_device() or a shared helper. Of course
we would then have to properly document zdev->fmb_enabled as being a
the target rather than current state.

Thanks,
Niklas

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v8 2/4] s390/pci: Reuse FMB buffer and preserve state in device re-enablement
  2026-10-05 19:53   ` Niklas Schnelle
@ 2026-10-05 22:05     ` Omar Elghoul
  0 siblings, 0 replies; 8+ messages in thread
From: Omar Elghoul @ 2026-10-05 22:05 UTC (permalink / raw)
  To: Niklas Schnelle, linux-s390, linux-kernel, kvm
  Cc: hca, gor, agordeev, borntraeger, svens, mjrosato, alifm, farman,
	gbayer, pasic, alex, frankja, imbrenda

On 10/5/26 3:53 PM, Niklas Schnelle wrote:
> On Mon, 2026-10-05 at 11:45 -0400, Omar Elghoul wrote:
>> Don't free the FMB buffer when disabling measurement in
>> zpci_fmb_disable_device(). Instead, make the buffer persistent for the
>> lifetime of the device and reuse it across enable/disable cycles. Defer
>> freeing the buffer until teardown in zpci_release_device().
>>
>> To support the persistent buffers, add the fmb_enabled bool to struct
>> zpci_dev to decouple whether FMB is enabled from whether the buffer has
>> been allocated. Audit the only consumer of zdev->fmb as a liveness check
>> and update it to reflect this change.
>>
>> Introduce the function zpci_fmb_reenable_device() to ensure that the FMB
>> is enabled. If it was already enabled, disable it, zero the counters,
>> and re-enable it. This allows the function to be used in both first-time
>> enabling and re-enabling measurement. Call it in zpci_reenable_device()
>> to preserve the FMB enablement if it had been implicitly disabled by
>> firmware in zpci_disable_device().
> 
> I think this causes a sequencing error in zpci_hot_reset_device().
> First the device gets disabled via zpci_disable_device(). This
> implicitly disables the FMB but keeps zdev->fmb_enabled set. Then we
> call zpci_fmb_reenable_device() in zpci_reenable_device(). Since zdev-
>> fmb_enabled is set we don't first enable the FMB and instead go
> directly to disabling it but that is wrong since the FMB is already
> disabled as a side effect of the CLP Set PCI Function (Disable) in
> zpci_disable_device().
> 
> Also, and I think Gerd mentioned this before, there is a disconnect in
> semantics between zpci_fmb_reenable_device() and zpci_reenable_device()
> that is quite confusing. While zpci_reenable_device() re-enables the
> device with existing interrupts and I/O address translations, after it
> was disabled, zpci_fmb_reenable_device() on the other hand does a
> disable and then enable cycle.
> 
> I think the idea here is that zdev->fmb_enabled tries to track whether
> the FMB is supposed to be enabled rather than if it is enabled.  This
> makes some sense since the FMB can get disabled by the device entering
> the error state or a zpci_disable_device() and we want to know if we
> need to re-enable it at the re-enable of the device.
> 
> Importantly, unlike the disablement of a device we always initiate the
> enablement. But then we can't try to disable the FMB without knowing if
> it was already disabled. I think a possible solution for this would be
> to have zpci_fmb_reenable_device() mean that we know that the FMB is
> disabled but should be enabled, which we know when we re-enable the
> device and zdev->fmb_enabled is set. Of course then it doesn't do a
> disable but only an enable despite zdev->fmb_enabled already being set,
> Then zpci_fmb_enable_device() on the other hand sets the flag initially
> and then uses zpci_fmb_reenable_device() or a shared helper. Of course
> we would then have to properly document zdev->fmb_enabled as being a
> the target rather than current state.

I agree with your insight and I'd be happy to follow this approach, but
I think this can cause FMB consumers to read stale snapshots (e.g. if
the device was disabled due to an error state or similar but fmb_enabled
is true). What would you think of leaving fmb_enabled as-is to indicate
whether FMB is actually enabled, and then introducing a second bool,
maybe something like fmb_needed, to track the user's intent and whether
we should call zpci_fmb_reenable_device() from zpci_reenable_device()?

This way, a successful zpci_fmb_enable_device() sets both flags, and
zpci_fmb_disable_device() clears both. zpci_disable_device() should
only clear fmb_enabled and leave fmb_needed as-is, allowing us to track
the implicit disablement by the firmware. This will make fmb_enabled
represent the actual firmware truth, and it becomes a reliable liveness
check for the FMB consumers (debugfs and vfio, for now.)

As for zpci_reenable_device(), it would check fmb_needed and if set,
call zpci_fmb_reenable_device(), since we'd already know by that point
that the FMB was implicitly disabled by firmware.

Thanks

> 
> Thanks,
> Niklas


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-05 22:05 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-05 15:45 [PATCH v8 0/4] vfio-pci/zdev: Improved zPCI Function Measurement Support Omar Elghoul
2026-10-05 15:45 ` [PATCH v8 1/4] s390/pci: Hold fmb_lock when enabling or disabling PCI devices Omar Elghoul
2026-10-05 15:45 ` [PATCH v8 2/4] s390/pci: Reuse FMB buffer and preserve state in device re-enablement Omar Elghoul
2026-10-05 19:53   ` Niklas Schnelle
2026-10-05 22:05     ` Omar Elghoul
2026-10-05 15:45 ` [PATCH v8 3/4] s390/pci: Fence FMB enable/disable via debugfs for passthrough devices Omar Elghoul
2026-10-05 18:42   ` Niklas Schnelle
2026-10-05 15:45 ` [PATCH v8 4/4] vfio-pci/zdev: Add VFIO FMB device features Omar Elghoul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®