iommufd: TSM-backed virtual IOMMUs for confidential guests
Subsystems (14)
Aneesh Kumar K.V (Arm)
maintainerv8 cover iommufd: TSM-backed virtual IOMMUs for confidential guests
This series adds IOMMUFD and PCI/TSM infrastructure for assigning PCI devices to confidential guests. A TEE Security Manager (TSM) can provide a virtual IOMMU (vIOMMU), and an IOMMUFD vDEVICE object represents a physical device within that virtual IOMMU.
The series adds a common framework for operations on vDEVICE objects and updates lifetime management to keep the VM and device-security resources available while those objects are in use.
The Arm CCA backend is provided in a separate series, together with operations for TEE Device Interface Security Protocol (TDISP) state changes and access to cached device-attestation objects.
The full series, including the Arm CCA backend, is available at:
https://git.gitlab.arm.com/linux-arm/linux-cca.git cca/topics/7.4/tdisp-host
Notes:
* Codex was used to assist with commit message formatting and code rearrangement.
Changes from v7:
https://lore.kernel.org/all/[email protected]
* Replace IOMMU_VDEVICE_TSM_REQ with the common IOMMU_VDEVICE_OP ioctl framework. Keep operation definitions and callbacks in the Arm CCA backend series.
* Replace architecture-specific request dispatch, TSM result codes and buffer-residue returns with operation-specific request/response formats, a zero-success return convention and resp_len for response lengths.
* Hold a KVM file reference for the full iommufd_device lifetime and require matching device/vIOMMU KVM associations for vDEVICE allocation.
* Hold TSM device and user references as well as the backend module reference for the vIOMMU lifetime. Make unregister reject new users and wait for existing users before tearing down PCI connections.
* Coordinate DSM removal with TSM unregister so the completion used to wait for users remains valid throughout teardown.
* Complete PCI registration before publishing the TSM device and roll back PCI registration if publication fails. Require PCI operations when registering a TSM.
* Move PCI/TSM core code into drivers/pci/tsm/core.c.
* Temporarily disable AMD vIOMMU allocation while its interfaces are updated.
Changes from v6:
https://lore.kernel.org/all/[email protected]
* Replace the IOMMUFD provider registry with per-device TSM operation lookup.
* Move vIOMMU allocation size and initialization into the selected vIOMMU ops.
* Select parent HWPT policy from the vIOMMU ops flags.
* Pin a vIOMMU's TSM backend module through vIOMMU destruction.
Changes from v5:
https://lore.kernel.org/all/[email protected]
* Replace the TSM bind/unbind interface with reference-counted contexts.
* Add the IOMMUFD vIOMMU provider abstraction.
* Route TSM guest requests through vIOMMU ops.
Changes from v4:
https://lore.kernel.org/all/[email protected]
* Switch VFIO/iommufd to use struct file *kvm_file instead of relying on kvm->users_count references.
* Define TSM request scope values globally in iommufd.
* Rename the ioctl to IOMMU_VDEVICE_TSM_REQ.
* Address other review feedback.
Changes from v2:
https://lore.kernel.org/all/[email protected]
* Bump the series revision to v4 to keep it in sync with the dependent CCA DA patchsets. There was no v3 posting.
* Drop [PATCH v2 1/3] iommufd/viommu: Allow associating a KVM VM fd with a vIOMMU
* Add two new patches to associate a struct kvm * with iommufd objects:
iommufd/device: Associate a kvm pointer to iommufd_device iommufd/viommu: Associate a kvm pointer to iommufd_viommu
* Address review feedback
Changes from v1:
https://lore.kernel.org/all/[email protected]
* Rebase onto the latest kernel
* Address review feedback
* Drop the TSM map ioctl; the KVM prefault patch will be used instead to ensure that private memory is preallocated
Aneesh Kumar K.V (Arm) (13):
PCI/TSM: Move core code into its own directory
virt: coco: tsm: Remove the device from lookup before PCI teardown
virt: coco: tsm: Complete PCI registration before publishing the
device
virt: coco: tsm: Require PCI operations when registering a TSM iommufd/device: Hold a KVM file reference for the device lifetime
iommufd: Add a vDEVICE operation ioctl framework PCI/TSM: Remove the legacy guest request interface PCI/TSM: Add vIOMMU-bound device contexts iommufd/viommu: Select vIOMMU operations before allocation iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations iommufd/viommu: Allow vIOMMUs without a parent HWPT PCI/TSM: Wait for device contexts before removing a DSM PCI/TSM: Wait for users during unregister
Nicolin Chen (1):
iommufd/viommu: Keep a reference to the KVM file
Shameer Kolothum (1):
iommufd/device: Associate KVM file pointer with iommufd_device
Documentation/ABI/testing/sysfs-bus-pci | 15 +-
Documentation/driver-api/pci/tsm.rst | 2 +-
Documentation/userspace-api/iommufd.rst | 44 +-
MAINTAINERS | 2 +-
drivers/crypto/ccp/sev-dev-tsm.c | 4 +-
drivers/iommu/amd/iommu.c | 3 +-
drivers/iommu/amd/iommufd.c | 18 +-
drivers/iommu/amd/iommufd.h | 11 +-
drivers/iommu/amd/nested.c | 4 +-
.../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 37 +-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 7 +-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 16 +-
.../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 24 +-
drivers/iommu/iommufd/Makefile | 1 +
drivers/iommu/iommufd/device.c | 14 +-
drivers/iommu/iommufd/hw_pagetable.c | 14 +-
drivers/iommu/iommufd/iommufd_private.h | 3 +
drivers/iommu/iommufd/main.c | 3 +
drivers/iommu/iommufd/selftest.c | 33 +-
drivers/iommu/iommufd/viommu.c | 155 +++++--
drivers/pci/Makefile | 2 +-
drivers/pci/tsm/Makefile | 7 +
drivers/pci/{tsm.c => tsm/core.c} | 434 ++++++++++--------
drivers/vfio/iommufd.c | 3 +-
drivers/virt/coco/tsm-core.c | 202 +++++++-
include/linux/iommu.h | 20 +-
include/linux/iommufd.h | 29 +-
include/linux/pci-tsm.h | 154 +++----
include/linux/tsm.h | 41 +-
include/uapi/linux/iommufd.h | 36 +-
30 files changed, 884 insertions(+), 454 deletions(-)
create mode 100644 drivers/pci/tsm/Makefile
rename drivers/pci/{tsm.c => tsm/core.c} (71%)base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
Signature · 1 line
2.43.0
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 1/15 PCI/TSM: Move core code into its own directory
Multiple patch series modify drivers/pci/tsm.c, and some add new files to the PCI/TSM subsystem. Move tsm.c to tsm/core.c so these series can share the directory layout and reduce merge conflicts.
Update the build rules, local header include, and MAINTAINERS entry for the new location.
No functional change.
---
Documentation/driver-api/pci/tsm.rst | 2 +-
MAINTAINERS | 2 +-
drivers/pci/Makefile | 2 +-
drivers/pci/tsm/Makefile | 7 +++++++
drivers/pci/{tsm.c => tsm/core.c} | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
create mode 100644 drivers/pci/tsm/Makefile
rename drivers/pci/{tsm.c => tsm/core.c} (99%)Documentation/driver-api/pci/tsm.rst +1 -1
@@ -17,5 +17,5 @@ Subsystem Interfaces .. kernel-doc:: include/linux/pci-tsm.h :internal: -.. kernel-doc:: drivers/pci/tsm.c +.. kernel-doc:: drivers/pci/tsm/core.c :export:
MAINTAINERS +1 -1
@@ -28487,7 +28487,7 @@ S: Maintained F: Documentation/ABI/testing/configfs-tsm-report F: Documentation/driver-api/coco/ F: Documentation/driver-api/pci/tsm.rst -F: drivers/pci/tsm.c +F: drivers/pci/tsm/ F: drivers/virt/coco/guest/ F: include/linux/*tsm*.h F: samples/tsm-mr/
drivers/pci/Makefile +1 -1
@@ -35,7 +35,7 @@ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o obj-$(CONFIG_VGA_ARB) += vgaarb.o obj-$(CONFIG_PCI_DOE) += doe.o obj-$(CONFIG_PCI_IDE) += ide.o -obj-$(CONFIG_PCI_TSM) += tsm.o +obj-$(CONFIG_PCI_TSM) += tsm/ obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.o obj-$(CONFIG_PCIE_TPH) += tph.o
drivers/pci/tsm/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the PCI/TSM infrastructure + +obj-$(CONFIG_PCI_TSM) += tsm.o + +tsm-y := core.o
drivers/pci/tsm/core.c renamed from drivers/pci/tsm.c +1 -1
@@ -15,7 +15,7 @@#include <linux/sysfs.h> #include <linux/tsm.h> #include <linux/xarray.h> -#include "pci.h" +#include "../pci.h" /* * Provide a read/write lock against the init / exit of pdev tsm
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 01/15] PCI/TSM: Move core code into its own directory
Sashiko has reviewed this patch and found no issues.
Quoted 10 lines
Multiple patch series modify
drivers/pci/tsm.c, and some add new files to the PCI/TSM subsystem. Movetsm.ctotsm/core.cso these series can share the directory layout and reduce merge conflicts.Update the build rules, local header include, and
MAINTAINERSentry for the new location.No functional change.
Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
kernel test robot
build botRe: [PATCH v8 01/15] PCI/TSM: Move core code into its own directory
In reply to Aneesh Kumar K.V (Arm)kernel test robot noticed the following build warnings:
Hi Aneesh,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4]
url: https://github.com/intel-lab-lkp/linux/commits/Aneesh-Kumar-K-V-Arm/PCI-TSM-Move-core-code-into-its-own-directory/20261104-214618 base: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4 patch link: https://lore.kernel.org/r/20261104161632.3547423-2-aneesh.kumar%40kernel.org patch subject: [PATCH v8 01/15] PCI/TSM: Move core code into its own directory config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20261105/[email protected]/config) compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project 812626a41399a1d464a0d69627ab45c83b8ebfb9) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261105/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/pci/tsm/core.c:31:12: warning: variable 'pci_tsm_devsec_count' set but not used [-Wunused-but-set-global]
31 | static int pci_tsm_devsec_count;
| ^1 warning generated.
vim +/pci_tsm_devsec_count +31 drivers/pci/tsm/core.c
3225f52cde56f4 drivers/pci/tsm.c Dan Williams 2025-10-31 25 3225f52cde56f4 drivers/pci/tsm.c Dan Williams 2025-10-31 26 /* 3225f52cde56f4 drivers/pci/tsm.c Dan Williams 2025-10-31 27 * Count of TSMs registered that support physical link operations vs device 3225f52cde56f4 drivers/pci/tsm.c Dan Williams 2025-10-31 28 * security state management. 3225f52cde56f4 drivers/pci/tsm.c Dan Williams 2025-10-31 29 */ 3225f52cde56f4 drivers/pci/tsm.c Dan Williams 2025-10-31 30 static int pci_tsm_link_count;
3225f52cde56f4 drivers/pci/tsm.c Dan Williams 2025-10-31 @31 static int pci_tsm_devsec_count; 3225f52cde56f4 drivers/pci/tsm.c Dan Williams 2025-10-31 32
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 2/15 virt: coco: tsm: Remove the device from lookup before PCI teardown
In reply to Aneesh Kumar K.V (Arm)PCI connect looks up a TSM by ID under pci_tsm_rwsem. Previously, tsm_unregister() released that lock after tearing down PCI state while the TSM was still in the class lookup. A racing connect could then attach a new PCI context that the teardown would never see.
Remove the device from the class lookup first, then take the PCI write lock to drain in-flight operations and destroy their contexts. Drop the device reference only after PCI teardown completes.
--- drivers/virt/coco/tsm-core.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
drivers/virt/coco/tsm-core.c +14 -8
@@ -56,26 +56,25 @@ static struct tsm_dev *alloc_tsm_dev(struct device *parent)return no_free_ptr (tsm_dev); } -static struct tsm_dev *tsm_register_pci_or_reset (struct tsm_dev *tsm_dev, -struct pci_tsm_ops *pci_ops) +static int tsm_register_pci (struct tsm_dev *tsm_dev,struct pci_tsm_ops *pci_ops) {int rc;if (!pci_ops) -return tsm_dev; +return 0 ; tsm_dev->pci_ops = pci_ops; rc =pci_tsm_register (tsm_dev);if (rc) { + tsm_dev->pci_ops =NULL ;dev_err (tsm_dev->dev.parent,"PCI/TSM registration failure: %d\n" , rc); -device_unregister (&tsm_dev->dev); -return ERR_PTR (rc); +return rc; }/* Notify TSM userspace that PCI/TSM operations are now possible */ kobject_uevent (&tsm_dev->dev.kobj,KOBJ_CHANGE ); -return tsm_dev; +return 0 ; }struct tsm_dev *tsm_register (struct device *parent,struct pci_tsm_ops *pci_ops)
@@ -96,15 +95,22 @@ struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pci_ops)if (rc)return ERR_PTR (rc); -return tsm_register_pci_or_reset (no_free_ptr (tsm_dev), pci_ops); + rc =tsm_register_pci (tsm_dev, pci_ops); +if (rc) { +device_del (dev); +return ERR_PTR (rc); + } +return no_free_ptr (tsm_dev); }EXPORT_SYMBOL_GPL (tsm_register);void tsm_unregister (struct tsm_dev *tsm_dev) { +/* Remove the class lookup first. */ +device_del (&tsm_dev->dev);if (tsm_dev->pci_ops)pci_tsm_unregister (tsm_dev); -device_unregister (&tsm_dev->dev); +put_device (&tsm_dev->dev); }EXPORT_SYMBOL_GPL (tsm_unregister);
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 02/15] virt: coco: tsm: Remove the device from lookup before PCI teardown
Sashiko has reviewed this patch and found no issues.
Quoted 11 lines
PCI connect looks up a TSM by ID under
pci_tsm_rwsem. Previously,tsm_unregister()released that lock after tearing down PCI state while the TSM was still in the class lookup. A racing connect could then attach a new PCI context that the teardown would never see.Remove the device from the class lookup first, then take the PCI write lock to drain in-flight operations and destroy their contexts. Drop the device reference only after PCI teardown completes.
Fixes 3225f52cde56 ("PCI/TSM: Establish Secure Sessions and Link Encryption")Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
RE: [PATCH v8 02/15] virt: coco: tsm: Remove the device from lookup before PCI teardown
In reply to Aneesh Kumar K.V (Arm)Quoted 8 lines
From: Aneesh Kumar K.V (Arm) <[email protected]>
Sent: Thursday, November 5, 2026 12:16 AMPCI connect looks up a TSM by ID under
pci_tsm_rwsem. Previously,tsm_unregister()released that lock after tearing down PCI state while the TSM was still in the class lookup. A racing connect could then attach a new PCI context that the teardown would never see.
Quoted 6 lines
Remove the device from the class lookup first, then take the PCI write lock to drain in-flight operations and destroy their contexts. Drop the device reference only after PCI teardown completes.
Fixes 3225f52cde56 ("PCI/TSM: Establish Secure Sessions and Link Encryption")Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
btw this can be sent separately.
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 3/15 virt: coco: tsm: Complete PCI registration before publishing the device
In reply to Aneesh Kumar K.V (Arm)tsm_register() calls device_add() before initializing and validating the PCI operations. This makes the new TSM discoverable through the class lookup while PCI registration can still fail. This ordering was introduced by commit 3225f52cde56 ("PCI/TSM: Establish Secure Sessions and Link Encryption").
An existing link TSM can keep the PCI connect attribute visible during registration of another TSM. A concurrent write naming the new TSM can therefore reach it through:
connect_store()
find_tsm_dev()
class_find_device()
is_link_tsm()
pci_tsm_connect()If the new backend supplies both link and device security operations, pci_tsm_register() rejects it before taking pci_tsm_rwsem. The registration thread then follows:
tsm_register()
tsm_register_pci()
pci_tsm_register() -> -EINVAL
tsm_dev->pci_ops = NULL
device_del()
put_tsm_dev() [cleanup guard]If connect has already attached a PCI context, its temporary device reference does not protect the stored tsm_dev pointer after the call returns. Registration cleanup can then free a TSM still referenced by that context. The original tsm_register_pci_or_reset() error path also had this problem: device_unregister() removed the class device and dropped its reference without tearing down an attached PCI context.
Commit 37a73b0448ad ("tsm: Remove the device from lookup before PCI teardown") subsequently added the unlocked pci_ops reset shown above. That reset can also race with the connect path's accesses to pci_ops.
Complete PCI registration before device_add() publishes the class device. On PCI registration failure, the TSM remains undiscoverable. If device_add() fails instead, unregister the PCI operations to roll back the counters and PCI sysfs visibility before releasing the device.
With pci_ops initialized before device_add(), the class PCI attributes are created with the correct visibility, so remove the subsequent sysfs group update. Keep the PCI change notification after successful device publication.
--- drivers/virt/coco/tsm-core.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/virt/coco/tsm-core.c +10 -6
@@ -71,9 +71,6 @@ static int tsm_register_pci(struct tsm_dev *tsm_dev, struct pci_tsm_ops *pci_ops "PCI/TSM registration failure: %d\n", rc);return rc; } - -/* Notify TSM userspace that PCI/TSM operations are now possible */ -kobject_uevent (&tsm_dev->dev.kobj,KOBJ_CHANGE );return 0 ; }
@@ -91,15 +88,22 @@ struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pci_ops)if (rc)return ERR_PTR (rc); - rc =device_add (dev); +/* Complete PCI registration before exposing the TSM to class lookup. */ + rc =tsm_register_pci (tsm_dev, pci_ops);if (rc)return ERR_PTR (rc); - rc =tsm_register_pci (tsm_dev, pci_ops); + rc =device_add (dev);if (rc) { -device_del (dev); +if (pci_ops) +pci_tsm_unregister (tsm_dev);return ERR_PTR (rc); } + +/* Notify TSM userspace that PCI/TSM operations are now possible. */ +if (pci_ops) +kobject_uevent (&dev->kobj,KOBJ_CHANGE ); +return no_free_ptr (tsm_dev); }EXPORT_SYMBOL_GPL (tsm_register);
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 03/15] virt: coco: tsm: Complete PCI registration before publishing the device
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] virt: coco: tsm: stale sysfs update and commit reference in commit log --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] virt: coco: tsm: stale sysfs update and commit reference in commit log --
commit 5fae6df00051f68d8e86275946ecd6ac727fb344
Author: Aneesh Kumar K.V (Arm) <[email protected]>
virt: coco: tsm: Complete PCI registration before publishing the device
This commit moves PCI TSM registration ahead of device_add() in tsm_register() so that a TSM device is not discoverable through class lookup while PCI registration can still fail, and rolls back PCI registration if device_add() fails.
Quoted 45 lines
virt: coco: tsm: Complete PCI registration before publishing the device
tsm_register()callsdevice_add()before initializing and validating the PCI operations. This makes the new TSM discoverable through the class lookup while PCI registration can still fail. This ordering was introduced by commit3225f52cde56("PCI/TSM: Establish Secure Sessions and Link Encryption").An existing link TSM can keep the PCI connect attribute visible during registration of another TSM. A concurrent write naming the new TSM can therefore reach it through:
connect_store()
find_tsm_dev()
class_find_device()
is_link_tsm()
pci_tsm_connect()If the new backend supplies both link and device security operations,
pci_tsm_register()rejects it before takingpci_tsm_rwsem. The registration thread then follows:
tsm_register()
tsm_register_pci()
pci_tsm_register()->-EINVAL
tsm_dev->pci_ops= NULL
device_del()
put_tsm_dev()[cleanup guard]If connect has already attached a PCI context, its temporary device reference does not protect the stored
tsm_devpointer after the call returns. Registration cleanup can then free a TSM still referenced by that context. The originaltsm_register_pci_or_reset()error path also had this problem:device_unregister()removed the class device and dropped its reference without tearing down an attached PCI context.Commit
37a73b0448ad("tsm: Remove the device from lookup before PCI teardown") subsequently added the unlockedpci_opsreset shown above. That reset can also race with the connect path's accesses topci_ops.Complete PCI registration before
device_add()publishes the class device. On PCI registration failure, the TSM remains undiscoverable. Ifdevice_add()fails instead, unregister the PCI operations to roll back the counters and PCI sysfs visibility before releasing the device.
Quoted 7 lines
With
pci_opsinitialized beforedevice_add(), the class PCI attributes are created with the correct visibility, so remove the subsequent sysfs group update. Keep the PCI change notification after successful device publication.Fixes 3225f52cde56 ("PCI/TSM: Establish Secure Sessions and Link Encryption")Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
[Severity: Medium]
Does the commit message match the actual changes in drivers/virt/coco/tsm-core.c?
The final paragraph states that class PCI attributes are created with the correct visibility and that a subsequent sysfs group update is removed. However, in drivers/virt/coco/tsm-core.c, tsm_class only defines .name and .dev_release without any class PCI attributes or attribute groups:
static const struct class tsm_class = {
.name = "tsm",
.dev_release = tsm_release
};Neither tsm_register_pci() nor tsm_register() contained or removed a sysfs_update_group() call; the patch only reorders tsm_register_pci() before device_add(), adds pci_tsm_unregister() on the device_add() error path, and moves kobject_uevent(&dev->kobj, KOBJ_CHANGE) from tsm_register_pci() into tsm_register() after device_add().
Also, the commit message references commit 37a73b0448ad ("tsm: Remove the device from lookup before PCI teardown"), which does not exist in the commit history because the preceding commit in the series is 2ae926219f58 ("virt:
coco: tsm: Remove the device from lookup before PCI teardown"). Could the commit message be updated to remove the stale sysfs group description and reference the right commit?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 4/15 virt: coco: tsm: Require PCI operations when registering a TSM
In reply to Aneesh Kumar K.V (Arm)Reject a NULL pci_ops argument in tsm_register(). Every registered TSM then has PCI operations, so registration no longer needs separate paths for TSMs without them.
--- drivers/virt/coco/tsm-core.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
drivers/virt/coco/tsm-core.c +5 -7
@@ -60,9 +60,6 @@ static int tsm_register_pci(struct tsm_dev *tsm_dev, struct pci_tsm_ops *pci_ops {int rc; -if (!pci_ops) -return 0 ; - tsm_dev->pci_ops = pci_ops; rc =pci_tsm_register (tsm_dev);if (rc) {
@@ -80,6 +77,9 @@ struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pci_ops)struct device *dev;int rc; +if (!pci_ops) +return ERR_PTR (-EINVAL ); +if (IS_ERR (tsm_dev))return tsm_dev;
@@ -95,14 +95,12 @@ struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pci_ops) rc =device_add (dev);if (rc) { -if (pci_ops) -pci_tsm_unregister (tsm_dev); +pci_tsm_unregister (tsm_dev);return ERR_PTR (rc); }/* Notify TSM userspace that PCI/TSM operations are now possible. */ -if (pci_ops) -kobject_uevent (&dev->kobj,KOBJ_CHANGE ); +kobject_uevent (&dev->kobj,KOBJ_CHANGE );return no_free_ptr (tsm_dev); }
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 04/15] virt: coco: tsm: Require PCI operations when registering a TSM
Sashiko has reviewed this patch and found no issues.
Quoted 5 lines
Reject a NULL
pci_opsargument intsm_register(). Every registered TSM then has PCI operations, so registration no longer needs separate paths for TSMs without them.Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 5/15 iommufd/device: Associate KVM file pointer with iommufd_device
In reply to Aneesh Kumar K.V (Arm)From: Shameer Kolothum <[email protected]>
TSM vDevice support needs access to the KVM associated with a VFIO device after the device has been bound to iommufd.
Extend iommufd_device_bind() to accept the device's KVM file and store it in the iommufd_device. The KVM file reference is owned by VFIO and is already held for the duration of the device open path.
--- drivers/iommu/iommufd/device.c | 7 ++++++- drivers/iommu/iommufd/iommufd_private.h | 2 ++ drivers/iommu/iommufd/selftest.c | 2 +- drivers/vfio/iommufd.c | 3 ++- include/linux/iommufd.h | 4 +++- 5 files changed, 14 insertions(+), 4 deletions(-)
drivers/iommu/iommufd/device.c +6 -1
@@ -295,6 +295,7 @@ static int iommufd_bind_noiommu(struct iommufd_device *idev)* iommufd_device_bind - Bind a physical device to an iommu fd * @ictx: iommufd file descriptor * @dev: Pointer to a physical device struct +* @kvm_file: VM file if device belongs to a KVM VM * @id: Output ID number to return to userspace for this device * * A successful bind establishes an ownership over the device and returns
@@ -308,7 +309,9 @@ static int iommufd_bind_noiommu(struct iommufd_device *idev)* The caller must undo this with iommufd_device_unbind() */ struct iommufd_device *iommufd_device_bind (struct iommufd_ctx *ictx, -struct device *dev,u32 *id) +struct device *dev, +struct file *kvm_file, +u32 *id) {struct iommufd_device *idev;int rc;
@@ -319,6 +322,8 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx, idev->ictx = ictx; idev->dev = dev; +/* VFIO holds the file reference while the device is open. */ + idev->kvm_file = kvm_file;if (!iommufd_device_is_noiommu (idev)) rc =iommufd_bind_iommu (idev);
drivers/iommu/iommufd/iommufd_private.h +2 -0
@@ -509,6 +509,8 @@ struct iommufd_device {struct list_head group_item;/* always the physical device */ struct device *dev; +/* ..and the VM file if available */ +struct file *kvm_file;bool enforce_cache_coherency;struct iommufd_vdevice *vdev;bool destroying;
drivers/iommu/iommufd/selftest.c +1 -1
@@ -1071,7 +1071,7 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd, goto out_sobj; } - idev =iommufd_device_bind (ucmd->ictx, &sobj->idev.mock_dev->dev, + idev =iommufd_device_bind (ucmd->ictx, &sobj->idev.mock_dev->dev,NULL , &idev_id);if (IS_ERR (idev)) { rc =PTR_ERR (idev);
drivers/vfio/iommufd.c +2 -1
@@ -123,7 +123,8 @@ int vfio_iommufd_physical_bind(struct vfio_device *vdev, {struct iommufd_device *idev; - idev =iommufd_device_bind (ictx, vdev->dev, out_device_id); + idev =iommufd_device_bind (ictx, vdev->dev, vdev->kvm, + out_device_id);if (IS_ERR (idev))return PTR_ERR (idev); vdev->iommufd_device = idev;
include/linux/iommufd.h +3 -1
@@ -59,7 +59,9 @@ struct iommufd_object { };struct iommufd_device *iommufd_device_bind (struct iommufd_ctx *ictx, -struct device *dev,u32 *id); +struct device *dev, +struct file *kvm_file, +u32 *id);void iommufd_device_unbind (struct iommufd_device *idev); int iommufd_device_attach(struct iommufd_device *idev, ioasid_t pasid,
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 05/15] iommufd/device: Associate KVM file pointer with iommufd_device
Sashiko has reviewed this patch and found no issues.
Quoted 9 lines
From: Shameer Kolothum <[email protected]>
TSM vDevice support needs access to the KVM associated with a VFIO device after the device has been bound to iommufd.
Extend
iommufd_device_bind()to accept the device's KVM file and store it in theiommufd_device. The KVM file reference is owned by VFIO and is already held for the duration of the device open path.
Quoted 13 lines
Signed-off-by Shameer Kolothum <[email protected]>Reviewed-by Jason Gunthorpe <[email protected]>[nicolinc: fix build error in iommufd_test_mock_domain()]Signed-off-by Nicolin Chen <[email protected]>[aneesh.kumar: Switch to use kvm_file]Cc Jason Gunthorpe <[email protected]>Cc Kevin Tian <[email protected]>Cc Joerg Roedel <[email protected]>Cc Will Deacon <[email protected]>Cc Robin Murphy <[email protected]>Cc Alex Williamson <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
RE: [PATCH v8 05/15] iommufd/device: Associate KVM file pointer with iommufd_device
In reply to Aneesh Kumar K.V (Arm)Quoted 12 lines
From: Aneesh Kumar K.V (Arm) <[email protected]>
Sent: Thursday, November 5, 2026 12:16 AMFrom: Shameer Kolothum <[email protected]>
TSM vDevice support needs access to the KVM associated with a VFIO device after the device has been bound to iommufd.
Extend
iommufd_device_bind()to accept the device's KVM file and store it in theiommufd_device. The KVM file reference is owned by VFIO and is already held for the duration of the device open path.
Quoted 13 lines
Signed-off-by Shameer Kolothum <[email protected]>Reviewed-by Jason Gunthorpe <[email protected]>[nicolinc: fix build error in iommufd_test_mock_domain()]Signed-off-by Nicolin Chen <[email protected]>[aneesh.kumar: Switch to use kvm_file]Cc Jason Gunthorpe <[email protected]>Cc Kevin Tian <[email protected]>Cc Joerg Roedel <[email protected]>Cc Will Deacon <[email protected]>Cc Robin Murphy <[email protected]>Cc Alex Williamson <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
a nit:
drivers/iommu/iommufd/iommufd_private.h struct iommufd_device { · line 513
Quoted 6 lines
drivers/iommu/iommufd/iommufd_private.h +2
-0@@ -509,6 +509,8 @@ struct iommufd_device {struct list_head group_item;/* always the physical device */ struct device *dev; +/* ..and the VM file if available */ +struct file *kvm_file;
though the comment will be immediately replaced in next patch, let's remove the pointless "..and" here.
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 6/15 iommufd/device: Hold a KVM file reference for the device lifetime
In reply to Aneesh Kumar K.V (Arm)The KVM file stored in an iommufd_device currently relies on the reference held by VFIO while the device is open. Normally, unbind waits for outstanding device references before VFIO drops its KVM reference. However, REMOVE_WAIT only waits for 60 seconds. If that timeout expires, the device object remains in the iommufd xarray.
For example, an IOMMU_GET_HW_INFO ioctl can hold a device reference while blocked on a userspace buffer fault:
iommufd_fops_ioctl()
iommufd_get_hw_info()
iommufd_get_device()
iommufd_get_object()
iommufd_lock_obj() /* takes users and wait_cnt refs */
copy_to_user() / clear_user() /* may block before putting refs */
A concurrent final release of the VFIO device file takes this path:
vfio_device_fops_release()
vfio_df_unbind_iommufd()
vfio_df_close()
vfio_df_device_last_close()
vfio_df_iommufd_unbind()
vfio_iommufd_physical_unbind()
iommufd_device_unbind()
iommufd_object_destroy_user()
iommufd_object_remove(..., REMOVE_WAIT)
iommufd_object_dec_wait() /* timeout: -EBUSY */
WARN_ON(ret)
vfio_device_put_kvm()
fput(device->kvm)iommufd_object_destroy_user() warns on -EBUSY but does not propagate it:iommufd_device_unbind() returns void. Thus VFIO close effectively ignores the unbind failure and proceeds to drop its KVM file reference even though the iommufd_device is still alive and its ID remains usable.
Once the remaining KVM file references are released, a later ioctl on the still-open iommufd can access the stale pointer:
iommufd_fops_ioctl()
iommufd_viommu_alloc_ioctl()
iommufd_get_device() /* finds the surviving device ID */
get_file(idev->kvm_file) /* accesses the freed KVM file */
Take a KVM file reference when binding the device and release it when the iommufd_device is actually destroyed. Release it before the igroup check in the destructor so that bind failure cleanup is covered too. Document the ownership in the bind API and device structure.
--- drivers/iommu/iommufd/device.c | 11 +++++++++-- drivers/iommu/iommufd/iommufd_private.h | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-)
drivers/iommu/iommufd/device.c +9 -2
@@ -1,6 +1,7 @@// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES */ +#include <linux/file.h> #include <linux/iommu.h> #include <linux/iommufd.h> #include <linux/pci-ats.h>
@@ -212,6 +213,9 @@ void iommufd_device_destroy(struct iommufd_object *obj)struct iommufd_device *idev = container_of(obj, struct iommufd_device, obj); +if (idev->kvm_file) +fput (idev->kvm_file); +/* igroup is NULL when destroy called during bind error cleanup */ if (!idev->igroup)return ;
@@ -306,6 +310,9 @@ static int iommufd_bind_noiommu(struct iommufd_device *idev)* * Binding a PCI device places the entire RID under iommufd control. * +* If @kvm_file is provided, a reference is held until the device object is +* destroyed. +* * The caller must undo this with iommufd_device_unbind() */ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
@@ -322,8 +329,8 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx, idev->ictx = ictx; idev->dev = dev; -/* VFIO holds the file reference while the device is open. */ - idev->kvm_file = kvm_file; +/* Hold the VM file for the lifetime of this object. */ + idev->kvm_file = kvm_file ?get_file (kvm_file) :NULL ;if (!iommufd_device_is_noiommu (idev)) rc =iommufd_bind_iommu (idev);
drivers/iommu/iommufd/iommufd_private.h +1 -1
@@ -509,7 +509,7 @@ struct iommufd_device {struct list_head group_item;/* always the physical device */ struct device *dev; -/* ..and the VM file if available */ +/* Owned reference to the VM file, if available */ struct file *kvm_file;bool enforce_cache_coherency;struct iommufd_vdevice *vdev;
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 06/15] iommufd/device: Hold a KVM file reference for the device lifetime
Sashiko has reviewed this patch and found no issues.
Quoted 19 lines
The KVM file stored in an
iommufd_devicecurrently relies on the reference held by VFIO while the device is open. Normally, unbind waits for outstanding device references before VFIO drops its KVM reference. However,REMOVE_WAITonly waits for 60 seconds. If that timeout expires, the device object remains in the iommufd xarray.For example, an
IOMMU_GET_HW_INFOioctl can hold a device reference while blocked on a userspace buffer fault:
iommufd_fops_ioctl()
iommufd_get_hw_info()
iommufd_get_device()
iommufd_get_object()
iommufd_lock_obj()/* takes users andwait_cntrefs */
copy_to_user()/clear_user()/* may block before putting refs */
[ ... ]
check in the destructor so that bind failure cleanup is covered too. Document the ownership in the bind API and device structure.
Quoted 2 lines
Fixes d8bb80c8f0e5 ("iommufd/device: Associate KVM file pointer with iommufd_device")Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
Tian, Kevin
maintainerRE: [PATCH v8 06/15] iommufd/device: Hold a KVM file reference for the device lifetime
In reply to Aneesh Kumar K.V (Arm)Quoted 49 lines
From: Aneesh Kumar K.V (Arm) <[email protected]>
Sent: Thursday, November 5, 2026 12:16 AMThe KVM file stored in an
iommufd_devicecurrently relies on the reference held by VFIO while the device is open. Normally, unbind waits for outstanding device references before VFIO drops its KVM reference. However,REMOVE_WAITonly waits for 60 seconds. If that timeout expires, the device object remains in the iommufd xarray.For example, an
IOMMU_GET_HW_INFOioctl can hold a device reference while
blocked on a userspace buffer fault:
iommufd_fops_ioctl()
iommufd_get_hw_info()
iommufd_get_device()
iommufd_get_object()
iommufd_lock_obj()/* takes users andwait_cntrefs */
copy_to_user()/clear_user()/* may block before putting refs */A concurrent final release of the VFIO device file takes this path:
vfio_device_fops_release()vfio_df_unbind_iommufd() vfio_df_close() vfio_df_device_last_close() vfio_df_iommufd_unbind() vfio_iommufd_physical_unbind() iommufd_device_unbind() iommufd_object_destroy_user() iommufd_object_remove(..., REMOVE_WAIT) iommufd_object_dec_wait() /* timeout: -EBUSY */ WARN_ON(ret) vfio_device_put_kvm() fput(device->kvm)
iommufd_object_destroy_user()warns on-EBUSYbut does not propagate it:iommufd_device_unbind()returns void. Thus VFIO close effectively ignores the unbind failure and proceeds to drop its KVM file reference even though theiommufd_deviceis still alive and its ID remains usable.Once the remaining KVM file references are released, a later ioctl on the still-open iommufd can access the stale pointer:
iommufd_fops_ioctl()
iommufd_viommu_alloc_ioctl()
iommufd_get_device()/* finds the surviving device ID */
get_file(idev->kvm_file)/* accesses the freed KVM file */
Quoted 4 lines
Take a KVM file reference when binding the device and release it when the
iommufd_deviceis actually destroyed. Release it before the igroup check in the destructor so that bind failure cleanup is covered too. Document the ownership in the bind API and device structure.
I don't think this needs to be fixed. There is already a WARN_ON() in iommufd_object_destroy_user() and many bad things could happen afterwards...
If we really think an ioctl could take more than 60s in sane scenarios, it's a more complex issue to be handled in iommufd core.
Quoted 3 lines
Fixes d8bb80c8f0e5 ("iommufd/device: Associate KVM file pointer with
iommufd_device")
btw no fix tag for an issue introduced by an earlier patch in the same series.
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 7/15 iommufd/viommu: Keep a reference to the KVM file
In reply to Aneesh Kumar K.V (Arm)From: Nicolin Chen <[email protected]>
The TSM vDevice operations need access to the KVM associated with the device's vIOMMU. Save the device's KVM file in the iommufd_viommu when the vIOMMU is allocated, and take a file reference so it remains valid for the lifetime of the vIOMMU.
Release the reference when the vIOMMU is destroyed.
Reject vdevice allocation when the device's KVM association differs from the vIOMMU's, including when only one has a KVM association. Allow allocation when both associations are NULL.
Based on an original patch by
Shameer Kolothum <[email protected]>
--- drivers/iommu/iommufd/viommu.c | 10 ++++++++++ include/linux/iommufd.h | 1 + 2 files changed, 11 insertions(+)
drivers/iommu/iommufd/viommu.c +10 -0
@@ -1,6 +1,7 @@// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES */ +#include <linux/file.h> #include "iommufd_private.h" void iommufd_viommu_destroy (struct iommufd_object *obj)
@@ -11,6 +12,8 @@ void iommufd_viommu_destroy(struct iommufd_object *obj)if (viommu->ops && viommu->ops->destroy) viommu->ops->destroy (viommu);refcount_dec (&viommu->hwpt->common.obj.users); +if (viommu->kvm_file) +fput (viommu->kvm_file);xa_destroy (&viommu->vdevs); }
@@ -82,6 +85,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) }xa_init (&viommu->vdevs); +if (idev->kvm_file) + viommu->kvm_file =get_file (idev->kvm_file); viommu->type = cmd->type; viommu->ictx = ucmd->ictx; viommu->hwpt = hwpt_paging;
@@ -171,6 +176,11 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) goto out_put_viommu; } +if (idev->kvm_file != viommu->kvm_file) { + rc = -EINVAL ; + goto out_put_idev; + } + iommu_dev =iommufd_device_get_iommu_dev (idev);if (!iommu_dev || viommu->iommu_dev != iommu_dev) { rc = -EINVAL ;
include/linux/iommufd.h +1 -0
@@ -103,6 +103,7 @@ struct iommufd_viommu {struct iommufd_ctx *ictx;struct iommu_device *iommu_dev;struct iommufd_hwpt_paging *hwpt; +struct file *kvm_file;const struct iommufd_viommu_ops *ops;
Signature · 1 line
2.43.0
Re: [PATCH v8 07/15] iommufd/viommu: Keep a reference to the KVM file
Hi Aneesh,
Quoted 78 lines · On Wed, 4 Nov 2026 21:46:24 +0530 "Aneesh Kumar K.V (Arm)"…
From: Nicolin Chen <[email protected]>
The TSM vDevice operations need access to the KVM associated with the device's vIOMMU. Save the device's KVM file in the
iommufd_viommuwhen the vIOMMU is allocated, and take a file reference so it remains valid for the lifetime of the vIOMMU.Release the reference when the vIOMMU is destroyed.
Reject vdevice allocation when the device's KVM association differs from the vIOMMU's, including when only one has a KVM association. Allow allocation when both associations are NULL.
Based on an original patch by
Shameer Kolothum <[email protected]>[nicolinc: hold kvm's users_count]Signed-off-by Nicolin Chen <[email protected]>[aneesh.kumar: Switch to use kvm_file]Cc Jason Gunthorpe <[email protected]>Cc Kevin Tian <[email protected]>Cc Joerg Roedel <[email protected]>Cc Will Deacon <[email protected]>Cc Robin Murphy <[email protected]>Reviewed-by Jason Gunthorpe <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>--- drivers/iommu/iommufd/viommu.c | 10 ++++++++++ include/linux/iommufd.h | 1 + 2 files changed, 11 insertions(+)(quoted diff) +0
-0
b/drivers/iommu/iommufd/viommu.cindexf7951057a1e5..0dbd382eeb98100644 ---a/drivers/iommu/iommufd/viommu.c
+++b/drivers/iommu/iommufd/viommu.cdrivers/iommu/iommufd/viommu.c +10
-0@@ -1,6 +1,7 @@// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES */ +#include <linux/file.h> #include "iommufd_private.h" void iommufd_viommu_destroy (struct iommufd_object *obj)@@ -11,6 +12,8 @@ void iommufd_viommu_destroy(struct iommufd_object *obj) if (viommu->ops && viommu->ops->destroy) viommu->ops->destroy (viommu);refcount_dec (&viommu->hwpt->common.obj.users); +if (viommu->kvm_file) +fput (viommu->kvm_file);xa_destroy (&viommu->vdevs); }@@ -82,6 +85,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) }xa_init (&viommu->vdevs); +if (idev->kvm_file) + viommu->kvm_file =get_file (idev->kvm_file); viommu->type = cmd->type; viommu->ictx = ucmd->ictx; viommu->hwpt = hwpt_paging;@@ -171,6 +176,11 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) goto out_put_viommu; } +if (idev->kvm_file != viommu->kvm_file) { + rc = -EINVAL ; + goto out_put_idev; + } + iommu_dev =iommufd_device_get_iommu_dev (idev);if (!iommu_dev || viommu->iommu_dev != iommu_dev) { rc = -EINVAL ;include/linux/iommufd.h +0
-0
include/linux/iommufd.h struct iommufd_viommu { · line 109
Quoted 7 lines
include/linux/iommufd.h +1
-0@@ -103,6 +103,7 @@ struct iommufd_viommu {struct iommufd_ctx *ictx;struct iommu_device *iommu_dev;struct iommufd_hwpt_paging *hwpt; +struct file *kvm_file;const struct iommufd_viommu_ops *ops;
We may need to enable non-kvm file (such as mshv) eventually, but until then this looks good to me.
[email protected]
AI reviewRe: [PATCH v8 07/15] iommufd/viommu: Keep a reference to the KVM file
In reply to Aneesh Kumar K.V (Arm)Sashiko has reviewed this patch and found no issues.
Quoted 16 lines
From: Nicolin Chen <[email protected]>
The TSM vDevice operations need access to the KVM associated with the device's vIOMMU. Save the device's KVM file in the
iommufd_viommuwhen the vIOMMU is allocated, and take a file reference so it remains valid for the lifetime of the vIOMMU.Release the reference when the vIOMMU is destroyed.
Reject vdevice allocation when the device's KVM association differs from the vIOMMU's, including when only one has a KVM association. Allow allocation when both associations are NULL.
Based on an original patch by
Shameer Kolothum <[email protected]>
Quoted 10 lines
[nicolinc: hold kvm's users_count]Signed-off-by Nicolin Chen <[email protected]>[aneesh.kumar: Switch to use kvm_file]Cc Jason Gunthorpe <[email protected]>Cc Kevin Tian <[email protected]>Cc Joerg Roedel <[email protected]>Cc Will Deacon <[email protected]>Cc Robin Murphy <[email protected]>Reviewed-by Jason Gunthorpe <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
RE: [PATCH v8 07/15] iommufd/viommu: Keep a reference to the KVM file
In reply to Aneesh Kumar K.V (Arm)Quoted 20 lines
From: Aneesh Kumar K.V (Arm) <[email protected]>
Sent: Thursday, November 5, 2026 12:16 AMFrom: Nicolin Chen <[email protected]>
The TSM vDevice operations need access to the KVM associated with the device's vIOMMU. Save the device's KVM file in the
iommufd_viommuwhen the
vIOMMU is allocated, and take a file reference so it remains valid for the lifetime of the vIOMMU.Release the reference when the vIOMMU is destroyed.
Reject vdevice allocation when the device's KVM association differs from the vIOMMU's, including when only one has a KVM association. Allow allocation when both associations are NULL.
Based on an original patch by
Shameer Kolothum <[email protected]>
Quoted 10 lines
[nicolinc: hold kvm's users_count]Signed-off-by Nicolin Chen <[email protected]>[aneesh.kumar: Switch to use kvm_file]Cc Jason Gunthorpe <[email protected]>Cc Kevin Tian <[email protected]>Cc Joerg Roedel <[email protected]>Cc Will Deacon <[email protected]>Cc Robin Murphy <[email protected]>Reviewed-by Jason Gunthorpe <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 8/15 iommufd: Add a vDEVICE operation ioctl framework
In reply to Aneesh Kumar K.V (Arm)Add IOMMU_VDEVICE_OP to execute operations on a vDEVICE using separate request and response buffers. Each operation defines its request and response formats, with response capacity checked before changing device state.
Look up the vDEVICE and check its supported_ops mask before dispatching an operation. Reject out-of-range operation numbers before testing the corresponding bit. Backends set the mask during vdevice_init().
This patch defines no operations. The default dispatch returns -EOPNOTSUPP; later patches can add operations independently.
--- drivers/iommu/iommufd/Makefile | 1 + drivers/iommu/iommufd/iommufd_private.h | 1 + drivers/iommu/iommufd/main.c | 3 +++ drivers/iommu/iommufd/viommu.c | 36 +++++++++++++++++++++++++ include/linux/iommufd.h | 3 +++ include/linux/tsm.h | 1 + include/uapi/linux/iommufd.h | 33 +++++++++++++++++++++++ 7 files changed, 78 insertions(+)
drivers/iommu/iommufd/Makefile +1 -0
@@ -11,6 +11,7 @@ iommufd-y := \ viommu.o iommufd-$(CONFIG_IOMMUFD_NOIOMMU) += hwpt_noiommu.o + iommufd-$(CONFIG_IOMMUFD_TEST) += selftest.o obj-$(CONFIG_IOMMUFD) += iommufd.o
drivers/iommu/iommufd/iommufd_private.h +1 -0
@@ -730,6 +730,7 @@ void iommufd_vdevice_destroy(struct iommufd_object *obj);void iommufd_vdevice_abort (struct iommufd_object *obj);int iommufd_hw_queue_alloc_ioctl (struct iommufd_ucmd *ucmd);void iommufd_hw_queue_destroy (struct iommufd_object *obj); +int iommufd_vdevice_op_ioctl (struct iommufd_ucmd *ucmd);#ifdef CONFIG_IOMMUFD_TEST int iommufd_test (struct iommufd_ucmd *ucmd);
drivers/iommu/iommufd/main.c +3 -0
@@ -453,6 +453,7 @@ union ucmd_buffer {struct iommu_veventq_alloc veventq;struct iommu_vfio_ioas vfio_ioas;struct iommu_viommu_alloc viommu; +struct iommu_vdevice_op vdevice_op;#ifdef CONFIG_IOMMUFD_TEST struct iommu_test_cmd test;#endif
@@ -516,6 +517,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { __reserved),IOCTL_OP (IOMMU_VIOMMU_ALLOC ,iommufd_viommu_alloc_ioctl ,struct iommu_viommu_alloc ,out_viommu_id ), +IOCTL_OP (IOMMU_VDEVICE_OP ,iommufd_vdevice_op_ioctl , +struct iommu_vdevice_op ,resp_uptr ), #ifdef CONFIG_IOMMUFD_TEST IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last), #endif
drivers/iommu/iommufd/viommu.c +36 -0
@@ -1,7 +1,11 @@// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES */ +#include <linux/bits.h> #include <linux/file.h> +#include <linux/cleanup.h> +#include <linux/tsm.h> +#include "iommufd_private.h" void iommufd_viommu_destroy (struct iommufd_object *obj)
@@ -121,6 +125,38 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd)return rc; } +int iommufd_vdevice_op_ioctl (struct iommufd_ucmd *ucmd) +{ +struct iommu_vdevice_op *cmd = ucmd->cmd; +struct iommufd_object *obj; +struct iommufd_vdevice *vdev; +int ret; + +if (cmd->reserved) +return -EINVAL ; + + obj =iommufd_get_object (ucmd->ictx, cmd->vdevice_id, +IOMMUFD_OBJ_VDEVICE ); +if (IS_ERR (obj)) +return PTR_ERR (obj); + vdev = container_of(obj, struct iommufd_vdevice, obj); + +if (cmd->op >=BITS_PER_TYPE (vdev->supported_ops) || + !(vdev->supported_ops &BIT_ULL (cmd->op))) { + ret = -EOPNOTSUPP ; + goto out_put_object; + } + +switch (cmd->op) { +default : + ret = -EOPNOTSUPP ; + } + +out_put_object: +iommufd_put_object (ucmd->ictx, obj); +return ret; +} +void iommufd_vdevice_abort (struct iommufd_object *obj) { struct iommufd_vdevice *vdev =
include/linux/iommufd.h +3 -0
@@ -125,6 +125,9 @@ struct iommufd_vdevice {*/ u64 virt_id; +/* Supported vDEVICE operations; set by vdevice_init(). */ +u64 supported_ops; +/* Clean up all driver-specific parts of an iommufd_vdevice */ void (*destroy)(struct iommufd_vdevice *vdev); };
include/linux/tsm.h +1 -0
@@ -6,6 +6,7 @@#include <linux/types.h> #include <linux/uuid.h> #include <linux/device.h> +#include <uapi/linux/iommufd.h> #define TSM_REPORT_INBLOB_MAX 64#define TSM_REPORT_OUTBLOB_MAX SZ_16M
include/uapi/linux/iommufd.h +33 -0
@@ -58,6 +58,7 @@ enum {IOMMUFD_CMD_VEVENTQ_ALLOC =0x93 ,IOMMUFD_CMD_HW_QUEUE_ALLOC =0x94 ,IOMMUFD_CMD_IOAS_NOIOMMU_GET_PA =0x95 , + IOMMUFD_CMD_VDEVICE_OP = 0x96, };/**
@@ -1398,4 +1399,36 @@ struct iommu_hw_queue_alloc { __aligned_u64 length; };#define IOMMU_HW_QUEUE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HW_QUEUE_ALLOC) + +/** +* struct iommu_vdevice_op - ioctl(IOMMU_VDEVICE_OP) +* @size: sizeof(struct iommu_vdevice_op) +* @vdevice_id: vDEVICE ID returned by IOMMU_VDEVICE_ALLOC +* @op: Operation number; no operations are currently defined +* @req_len: Size in bytes of the request at @req_uptr +* @resp_len: Response buffer capacity in bytes on input; bytes written on success +* @reserved: Must be zero +* @req_uptr: Userspace pointer to the operation-specific request +* @resp_uptr: Userspace pointer to the operation-specific response buffer +* +* Execute an operation on a vDEVICE. Each operation defines its request and +* response formats. Requests may be extended by appending fields; bytes beyond +* the kernel's supported request size must be zero. Response capacity must be +* checked before changing device state. The ioctl returns 0 on success or a +* negative error code. +* +* No operations are currently supported; the ioctl returns -EOPNOTSUPP. +*/ +struct iommu_vdevice_op { +__u32 size; +__u32 vdevice_id; +__u32 op; +__u32 req_len; +__u32 resp_len; +__u32 reserved; +__aligned_u64 req_uptr; +__aligned_u64 resp_uptr; +}; + +#define IOMMU_VDEVICE_OP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VDEVICE_OP)#endif
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 08/15] iommufd: Add a vDEVICE operation ioctl framework
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] iommufd: iommufd_vdevice_op_ioctl() returns -EINVAL for non-zero reserved field --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] iommufd: iommufd_vdevice_op_ioctl() returns -EINVAL for non-zero reserved field --
commit 02d0f32280b8147168fe9f8c2300f3939bdda343
Author: Aneesh Kumar K.V (Arm) <[email protected]>
iommufd: Add a vDEVICE operation ioctl framework
This commit introduces the IOMMU_VDEVICE_OP ioctl and its dispatcher iommufd_vdevice_op_ioctl() to execute operations on a vDEVICE object using request and response buffers. It adds a supported_ops bitmask to struct iommufd_vdevice and returns -EOPNOTSUPP by default when no operations are defined.
Quoted 4 lines
drivers/iommu/iommufd/viommu.c +0
-0
[ ... ]
drivers/iommu/iommufd/viommu.c int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 136
Quoted 13 lines
drivers/iommu/iommufd/viommu.c +9
-0@@ -121,6 +125,38 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd)return rc; } +int iommufd_vdevice_op_ioctl (struct iommufd_ucmd *ucmd) +{ +struct iommu_vdevice_op *cmd = ucmd->cmd; +struct iommufd_object *obj; +struct iommufd_vdevice *vdev; +int ret; + +if (cmd->reserved) +return -EINVAL ;
[Severity: Medium]
Should iommufd_vdevice_op_ioctl() return -EOPNOTSUPP instead of -EINVAL when cmd->reserved is non-zero?
The general ioctl format documentation in include/uapi/linux/iommufd.h specifies that -EOPNOTSUPP is returned when a known field has a value the kernel does not understand or support, whereas -EINVAL indicates a field is not correct:
include/uapi/linux/iommufd.h:
* - EOPNOTSUPP: The IOCTL number is supported, and the structure is
* understood, however a known field has a value the kernel does not
* understand or support.
* - EINVAL: Everything about the IOCTL was understood, but a field is not
* correct.
All other ioctl handlers across drivers/iommu/iommufd/ return -EOPNOTSUPP when a reserved field is non-zero so userspace can detect whether a future kernel extension repurposing that field is supported.
Additionally, in include/uapi/linux/iommufd.h, struct iommu_vdevice_op names this member reserved:
struct iommu_vdevice_op {
...
__u32 reserved;
...
};Should this member be named __reserved to match the other UAPI structures in include/uapi/linux/iommufd.h?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
Tian, Kevin
maintainerRE: [PATCH v8 08/15] iommufd: Add a vDEVICE operation ioctl framework
In reply to Aneesh Kumar K.V (Arm)drivers/iommu/iommufd/Makefile iommufd-y := \ · line 15
Quoted 9 lines
From: Aneesh Kumar K.V (Arm) <[email protected]>
Sent: Thursday, November 5, 2026 12:16 AMdrivers/iommu/iommufd/Makefile +1
-0@@ -11,6 +11,7 @@ iommufd-y := \ viommu.o iommufd-$(CONFIG_IOMMUFD_NOIOMMU) += hwpt_noiommu.o + iommufd-$(CONFIG_IOMMUFD_TEST) += selftest.o
unnecessary blank line
drivers/iommu/iommufd/main.c union ucmd_buffer { · line 456
Quoted 5 lines
drivers/iommu/iommufd/main.c +1
-0@@ -453,6 +453,7 @@ union ucmd_buffer {struct iommu_veventq_alloc veventq;struct iommu_vfio_ioas vfio_ioas;struct iommu_viommu_alloc viommu; +struct iommu_vdevice_op vdevice_op;
in alphabetical order
drivers/iommu/iommufd/main.c static const struct iommufd_ioctl_op · line 522
Quoted 7 lines
drivers/iommu/iommufd/main.c +2
-0@@ -516,6 +517,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { __reserved), IOCTL_OP(IOMMU_VIOMMU_ALLOC, iommufd_viommu_alloc_ioctl, struct iommu_viommu_alloc, out_viommu_id), +IOCTL_OP (IOMMU_VDEVICE_OP , iommufd_vdevice_op_ioctl, + struct iommu_vdevice_op, resp_uptr),
ditto
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 9/15 PCI/TSM: Remove the legacy guest request interface
In reply to Aneesh Kumar K.V (Arm)Guest TSM requests can now be dispatched through the IOMMUFD IOMMU_VDEVICE_OP ioctl. Remove the PCI-device-based guest request entry point, its scope enum and the corresponding PCI/TSM driver callback.
--- drivers/pci/tsm/core.c | 60 ---------------------------------------- include/linux/pci-tsm.h | 61 +---------------------------------------- 2 files changed, 1 insertion(+), 120 deletions(-)
drivers/pci/tsm/core.c +0 -60
@@ -375,66 +375,6 @@ int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id) }EXPORT_SYMBOL_GPL (pci_tsm_bind); -/** -* pci_tsm_guest_req() - helper to marshal guest requests to the TSM driver -* @pdev: @pdev representing a bound tdi -* @scope: caller asserts this passthrough request is limited to TDISP operations -* @req_in: Input payload forwarded from the guest -* @in_len: Length of @req_in -* @req_out: Output payload buffer response to the guest -* @out_len: Length of @req_out on input, bytes filled in @req_out on output -* @tsm_code: Optional TSM arch specific result code for the guest TSM -* -* This is a common entry point for requests triggered by userspace KVM-exit -* service handlers responding to TDI information or state change requests. The -* scope parameter limits requests to TDISP state management, or limited debug. -* This path is only suitable for commands and results that are the host kernel -* has no use, the host is only facilitating guest to TSM communication. -* -* Returns 0 on success and -error on failure and positive "residue" on success -* but @req_out is filled with less then @out_len, or @req_out is NULL and a -* residue number of bytes were not consumed from @req_in. On success or -* failure @tsm_code may be populated with a TSM implementation specific result -* code for the guest to consume. -* -* Context: Caller is responsible for calling this within the pci_tsm_bind() -* state of the TDI. -*/ -ssize_t pci_tsm_guest_req (struct pci_dev *pdev,enum pci_tsm_req_scope scope, -sockptr_t req_in,size_t in_len,sockptr_t req_out, -size_t out_len,u64 *tsm_code) -{ -struct pci_tsm_pf0 *tsm_pf0; -struct pci_tdi *tdi; -int rc; - -/* Forbid requests that are not directly related to TDISP operations */ -if (scope >PCI_TSM_REQ_STATE_CHANGE ) -return -EINVAL ; - -ACQUIRE (rwsem_read_intr, lock)(&pci_tsm_rwsem); -if ((rc =ACQUIRE_ERR (rwsem_read_intr, &lock))) -return rc; - -if (!pdev->tsm) -return -ENXIO ; - -if (!is_link_tsm (pdev->tsm->tsm_dev)) -return -ENXIO ; - - tsm_pf0 =to_pci_tsm_pf0 (pdev->tsm); -ACQUIRE (mutex_intr, ops_lock)(&tsm_pf0->lock); -if ((rc =ACQUIRE_ERR (mutex_intr, &ops_lock))) -return rc; - - tdi = pdev->tsm->tdi; -if (!tdi) -return -ENXIO ; -return to_pci_tsm_ops (pdev->tsm)->guest_req (tdi, scope, req_in, in_len, - req_out, out_len, tsm_code); -} -EXPORT_SYMBOL_GPL (pci_tsm_guest_req); -static void pci_tsm_unbind_all (struct pci_dev *pdev) {pci_tsm_walk_fns_reverse (pdev, __pci_tsm_unbind,NULL );
include/linux/pci-tsm.h +1 -60
106 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 09/15] PCI/TSM: Remove the legacy guest request interface
Sashiko has reviewed this patch and found no issues.
Quoted 9 lines
Guest TSM requests can now be dispatched through the IOMMUFD
IOMMU_VDEVICE_OPioctl. Remove the PCI-device-based guest request entry point, its scope enum and the corresponding PCI/TSM driver callback.Cc Bjorn Helgaas <[email protected]>Reviewed-by Jason Gunthorpe <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 10/15 PCI/TSM: Add vIOMMU-bound device contexts
In reply to Aneesh Kumar K.V (Arm)An IOMMUFD vDEVICE represents a physical device within a virtual IOMMU (vIOMMU) for a guest. It must use the same TEE Security Manager as its vIOMMU. Pinning the vIOMMU backend keeps its tsm_dev registered, but does not preserve the connection between the physical function 0 (PF0) PCI device and that TSM.
Add a per-vDEVICE PCI TSM context. Verify that the PCI device and the vIOMMU use the same tsm_dev, and account active contexts on PF0. Reject disconnect with -EBUSY until all dependent vDEVICEs release their contexts.
With vDEVICE binding handled by the backend, PCI/TSM no longer has generic bind state to report. Remove the legacy bind and unbind interface. The sysfs "bound" attribute reported the state maintained by that interface. Remove the attribute and its ABI documentation as well.
--- Documentation/ABI/testing/sysfs-bus-pci | 15 +- drivers/pci/tsm/core.c | 230 ++++++++++-------------- include/linux/pci-tsm.h | 71 +++++--- 3 files changed, 135 insertions(+), 181 deletions(-)
Documentation/ABI/testing/sysfs-bus-pci +1 -14
@@ -660,6 +660,7 @@ Contact: [email protected] Description: (WO) Write the name of the TSM device that was specified to 'connect' to teardown the connection. + The write fails with EBUSY if the TSM is in use. What: /sys/bus/pci/devices/.../tsm/dsm Contact: [email protected]
@@ -677,20 +678,6 @@ Description: (RO) Return PCI device name of this device's DSM (Device PCIe switch port. This is a "link" TSM attribute, see Documentation/ABI/testing/sysfs-class-tsm. -What: /sys/bus/pci/devices/.../tsm/bound -Contact: [email protected] -Description: (RO) Return the device name of the TSM when the device is in a - TDISP (TEE Device Interface Security Protocol) operational state - (LOCKED, RUN, or ERROR, not UNLOCKED). Bound devices consume - platform TSM resources and depend on the device's configuration - (e.g. BME (Bus Master Enable) and MSE (Memory Space Enable) - among other settings) to remain stable for the duration of the - bound state. This attribute is only visible for devices that - support TDISP operation, and it is only populated after - successful connect and TSM bind. The TSM bind operation is - initiated by VFIO/IOMMUFD. This is a "link" TSM attribute, see - Documentation/ABI/testing/sysfs-class-tsm. - What: /sys/bus/pci/devices/.../authenticated Contact: [email protected] Description:
drivers/pci/tsm/core.c +92 -138
317 lines not on this page · raw message
include/linux/pci-tsm.h +42 -29
140 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 10/15] PCI/TSM: Add vIOMMU-bound device contexts
Sashiko has reviewed this patch and found no issues.
Quoted 11 lines
An IOMMUFD vDEVICE represents a physical device within a virtual IOMMU (vIOMMU) for a guest. It must use the same TEE Security Manager as its vIOMMU. Pinning the vIOMMU backend keeps its
tsm_devregistered, but does not preserve the connection between the physical function 0 (PF0) PCI device and that TSM.Add a per-vDEVICE PCI TSM context. Verify that the PCI device and the vIOMMU use the same
tsm_dev, and account active contexts on PF0. Reject disconnect with-EBUSYuntil all dependent vDEVICEs release their contexts.
Quoted 8 lines
With vDEVICE binding handled by the backend, PCI/TSM no longer has generic bind state to report. Remove the legacy bind and unbind interface. The sysfs "bound" attribute reported the state maintained by that interface. Remove the attribute and its ABI documentation as well.
Cc Bjorn Helgaas <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
Tian, Kevin
maintainerRE: [PATCH v8 10/15] PCI/TSM: Add vIOMMU-bound device contexts
In reply to Aneesh Kumar K.V (Arm)Quoted 9 lines
From: Aneesh Kumar K.V (Arm) <[email protected]>
Sent: Thursday, November 5, 2026 12:16 AMAn IOMMUFD vDEVICE represents a physical device within a virtual IOMMU (vIOMMU) for a guest. It must use the same TEE Security Manager as its vIOMMU. Pinning the vIOMMU backend keeps its
tsm_devregistered, but does not preserve the connection between the physical function 0 (PF0) PCI device and that TSM.
Quoted 4 lines
Add a per-vDEVICE PCI TSM context. Verify that the PCI device and the vIOMMU use the same
tsm_dev, and account active contexts on PF0. Reject disconnect with-EBUSYuntil all dependent vDEVICEs release their contexts.
We really don't need so many vIOMMU/vDEVICE notations here, especially in following code/comments. It's just about a tsm context which holds the pf0 from disconnected. Then let's describe/code it from that angle.
drivers/pci/tsm/core.c static int remove_fn(struct pci_dev *pdev, void *data) · line 311
Quoted 2 lines
drivers/pci/tsm/core.c +2
-0+struct pci_tsm_context *pci_tsm_context_get (struct pci_dev *pdev, +struct tsm_dev *viommu_tsm_dev)
could clarify that each get returns a new context even on a same device.
s/viommu_tsm_dev/tsm_dev/. It's just a parameter to be matched against.
Quoted 17 lines
{
drivers/pci/tsm/core.c +6
-5-struct pci_tsm_pf0 *tsm_pf0; -struct pci_tdi *tdi; - -if (!kvm) -return -EINVAL ; +struct pci_tsm_context *context; +struct pci_tsm_pf0 *pf0;guard (rwsem_read)(&pci_tsm_rwsem); +if (!pdev->tsm || !is_link_tsm (pdev->tsm->tsm_dev)) +return ERR_PTR (-EOPNOTSUPP ); +/* +* The vdevice can use a different PCI function from VIOMMU_ALLOC. The
(quoted diff) +1
-0+ * module pin keeps the vIOMMU's TSM registered, but does notfreeze this
drivers/pci/tsm/core.c static int remove_fn(struct pci_dev *pdev, void *data) · line 323
Quoted 2 lines
drivers/pci/tsm/core.c +2
-0+* function's link. Match its current TSM before pinning the link below. +*/
this is the example needing a rewrite...
drivers/pci/tsm/core.c static int remove_fn(struct pci_dev *pdev, void *data) · line 329
Quoted 8 lines
drivers/pci/tsm/core.c +5
-2+if (pdev->tsm->tsm_dev != viommu_tsm_dev) +return ERR_PTR (-EXDEV ); -if (!pdev->tsm) -return -EINVAL ; + pf0 =to_pci_tsm_pf0 (pdev->tsm); +if (!pf0) +return ERR_PTR (-ENXIO );
No need to check the return error as other places do?
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 11/15 iommufd/viommu: Select vIOMMU operations before allocation
In reply to Aneesh Kumar K.V (Arm)Physical IOMMU drivers currently provide the vIOMMU size and initialization callbacks through iommu_ops. The initialization callback then selects and installs the corresponding iommufd_viommu_ops.
Add iommu_ops::get_viommu_ops() to select the operations from the physical device and requested vIOMMU type before allocation. Move the size and initialization callbacks into iommufd_viommu_ops. The core can then validate the selected operations, allocate the driver structure, initialize it, and install the operations.
Convert AMD, Arm SMMUv3, Tegra CMDQV, and the selftest backend to the new interface.
Selecting the operations early allows subsequent changes to use implementation-specific vIOMMU properties, including when validating the parent HWPT.
Temporarily disable AMD vIOMMU allocation by returning NULL from amd_iommufd_get_viommu_ops() while the vIOMMU interfaces are being updated.
Cc: Joerg Roedel <[email protected]>
Cc: Suravee Suthikulpanit <[email protected]>
Cc: Vasant Hegde <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Robin Murphy <[email protected]>
Cc: Thierry Reding <[email protected]>
Cc: Krishna Reddy <[email protected]>
Cc: Jonathan Hunter <[email protected]>
Cc: Jason Gunthorpe <[email protected]>
Cc: Kevin Tian <[email protected]>
Cc: [email protected]
Cc: [email protected]
Based on original patch by Jason Gunthorpe <[email protected]>
--- drivers/iommu/amd/iommu.c | 3 +- drivers/iommu/amd/iommufd.c | 18 ++++++--- drivers/iommu/amd/iommufd.h | 11 ++++-- drivers/iommu/amd/nested.c | 4 +- .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 37 +++++++++++-------- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 7 ++-- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 16 ++++---- .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 24 ++++++++---- drivers/iommu/iommufd/selftest.c | 31 +++++++++++----- drivers/iommu/iommufd/viommu.c | 32 ++++++++-------- include/linux/iommu.h | 20 +++------- include/linux/iommufd.h | 14 +++++++ 12 files changed, 129 insertions(+), 88 deletions(-)
drivers/iommu/amd/iommu.c +1 -2
@@ -3442,8 +3442,7 @@ const struct iommu_ops amd_iommu_ops = { .is_attach_deferred = amd_iommu_is_attach_deferred, .def_domain_type = amd_iommu_def_domain_type, .page_response = amd_iommu_page_response, - .get_viommu_size = amd_iommufd_get_viommu_size, - .viommu_init = amd_iommufd_viommu_init, + .get_viommu_ops = amd_iommufd_get_viommu_ops, };#ifdef CONFIG_IRQ_REMAP
drivers/iommu/amd/iommufd.c +13 -5
@@ -32,13 +32,21 @@ void *amd_iommufd_hw_info(struct device *dev, u32 *length, enum iommu_hw_info_tyreturn hwinfo; } -size_t amd_iommufd_get_viommu_size (struct device *dev,enum iommu_viommu_type viommu_type) +static size_t amd_iommufd_get_viommu_size (struct device *dev, +enum iommu_viommu_type viommu_type) { return VIOMMU_STRUCT_SIZE(struct amd_iommu_viommu, core); } -int amd_iommufd_viommu_init (struct iommufd_viommu *viommu,struct iommu_domain *parent, -const struct iommu_user_data *user_data) +const struct iommufd_viommu_ops * +amd_iommufd_get_viommu_ops (struct device *dev,enum iommu_viommu_type viommu_type) +{ +return NULL ; +} + +int amd_iommufd_viommu_init (struct iommufd_viommu *viommu, +struct device *dev,struct iommu_domain *parent, +const struct iommu_user_data *user_data) {unsigned long flags;struct protection_domain *pdom =to_pdomain (parent);
@@ -47,8 +55,6 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *xa_init_flags (&aviommu->gdomid_array,XA_FLAGS_ALLOC1 ); aviommu->parent = pdom; - viommu->ops = &amd_viommu_ops; -spin_lock_irqsave (&pdom->lock, flags);list_add (&aviommu->pdom_list, &pdom->viommu_list);spin_unlock_irqrestore (&pdom->lock, flags);
@@ -73,5 +79,7 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)* struct iommufd_viommu_ops - vIOMMU specific operations */ static const struct iommufd_viommu_ops amd_viommu_ops = { + .get_viommu_size = amd_iommufd_get_viommu_size, + .viommu_init = amd_iommufd_viommu_init, .destroy = amd_iommufd_viommu_destroy, };
drivers/iommu/amd/iommufd.h +7 -4
@@ -8,13 +8,16 @@#if IS_ENABLED (CONFIG_AMD_IOMMU_IOMMUFD )void *amd_iommufd_hw_info (struct device *dev,u32 *length,enum iommu_hw_info_type *type); -size_t amd_iommufd_get_viommu_size (struct device *dev,enum iommu_viommu_type viommu_type); -int amd_iommufd_viommu_init (struct iommufd_viommu *viommu,struct iommu_domain *parent, -const struct iommu_user_data *user_data); +const struct iommufd_viommu_ops * +amd_iommufd_get_viommu_ops (struct device *dev, +enum iommu_viommu_type viommu_type); +int amd_iommufd_viommu_init (struct iommufd_viommu *viommu, +struct device *dev,struct iommu_domain *parent, +const struct iommu_user_data *user_data);#else #define amd_iommufd_hw_info NULL#define amd_iommufd_viommu_init NULL -#define amd_iommufd_get_viommu_size NULL +#define amd_iommufd_get_viommu_ops NULL#endif /* CONFIG_AMD_IOMMU_IOMMUFD */ #endif /* AMD_IOMMUFD_H */
drivers/iommu/amd/nested.c +2 -2
@@ -90,7 +90,7 @@ static void *gdom_info_load_or_alloc_locked(struct xarray *xa,/* * This function is assigned to struct iommufd_viommu_ops.alloc_domain_nested() -* during the call to struct iommu_ops.viommu_init(). +* when the vIOMMU operations are selected. */ struct iommu_domain * amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
@@ -198,7 +198,7 @@ static void set_dte_nested(struct amd_iommu *iommu, struct iommu_domain *dom,/* * The nest parent domain is attached during the call to the -* struct iommu_ops.viommu_init(), which will be stored as part +* struct iommufd_viommu_ops.viommu_init(), which will be stored as part * of the struct amd_iommu_viommu.parent. */ if (WARN_ON (!ndom->viommu || !ndom->viommu->parent))
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +22 -15
@@ -533,20 +533,29 @@ int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu,return ret; } +static size_t arm_vsmmu_get_size (struct device *dev, +enum iommu_viommu_type viommu_type) +{ + return VIOMMU_STRUCT_SIZE(struct arm_vsmmu, core); +} +static const struct iommufd_viommu_ops arm_vsmmu_ops = { + .get_viommu_size = arm_vsmmu_get_size, + .viommu_init = arm_vsmmu_init, .alloc_domain_nested = arm_vsmmu_alloc_domain_nested, .cache_invalidate = arm_vsmmu_cache_invalidate, .vdevice_init = arm_vsmmu_vdevice_init, }; -size_t arm_smmu_get_viommu_size (struct device *dev, -enum iommu_viommu_type viommu_type) +const struct iommufd_viommu_ops * +arm_smmu_get_viommu_ops (struct device *dev, +enum iommu_viommu_type viommu_type) {struct arm_smmu_master *master =dev_iommu_priv_get (dev);struct arm_smmu_device *smmu = master->smmu;if (!(smmu->features &ARM_SMMU_FEAT_NESTING )) -return 0 ; +return NULL ;/* * FORCE_SYNC is not set with FEAT_NESTING. Some study of the exact HW
@@ -554,7 +563,7 @@ size_t arm_smmu_get_viommu_size(struct device *dev,* any change to remove this. */ if (WARN_ON (smmu->options &ARM_SMMU_OPT_CMDQ_FORCE_SYNC )) -return 0 ; +return NULL ;/* * Must support some way to prevent the VM from bypassing the cache
@@ -566,19 +575,19 @@ size_t arm_smmu_get_viommu_size(struct device *dev,*/ if (!arm_smmu_master_canwbs (master) && !(smmu->features &ARM_SMMU_FEAT_S2FWB )) -return 0 ; +return NULL ;if (viommu_type ==IOMMU_VIOMMU_TYPE_ARM_SMMUV3 ) - return VIOMMU_STRUCT_SIZE(struct arm_vsmmu, core); +return &arm_vsmmu_ops; -if (!smmu->impl_ops || !smmu->impl_ops->get_viommu_size) -return 0 ; -return smmu->impl_ops->get_viommu_size (viommu_type); +if (!smmu->impl_ops || !smmu->impl_ops->get_viommu_ops) +return NULL ; +return smmu->impl_ops->get_viommu_ops (viommu_type); } -int arm_vsmmu_init (struct iommufd_viommu *viommu, -struct iommu_domain *parent_domain, -const struct iommu_user_data *user_data) +int arm_vsmmu_init (struct iommufd_viommu *viommu,struct device *dev, +struct iommu_domain *parent_domain, +const struct iommu_user_data *user_data) { struct arm_vsmmu *vsmmu = container_of(viommu, struct arm_vsmmu, core); struct arm_smmu_device *smmu =
@@ -593,10 +602,8 @@ int arm_vsmmu_init(struct iommufd_viommu *viommu,/* FIXME Move VMID allocation from the S2 domain allocation to here */ vsmmu->vmid = s2_parent->s2_cfg.vmid; -if (viommu->type ==IOMMU_VIOMMU_TYPE_ARM_SMMUV3 ) { - viommu->ops = &arm_vsmmu_ops; +if (viommu->type ==IOMMU_VIOMMU_TYPE_ARM_SMMUV3 )return 0 ; - }return smmu->impl_ops->vsmmu_init (vsmmu, user_data); }
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -4
@@ -4597,8 +4597,7 @@ static const struct iommu_ops arm_smmu_ops = { .get_resv_regions = arm_smmu_get_resv_regions, .page_response = arm_smmu_page_response, .def_domain_type = arm_smmu_def_domain_type, - .get_viommu_size = arm_smmu_get_viommu_size, - .viommu_init = arm_vsmmu_init, + .get_viommu_ops = arm_smmu_get_viommu_ops, .user_pasid_table = 1, .owner = THIS_MODULE, .default_domain_ops = &(const struct iommu_domain_ops ) {
@@ -5761,8 +5760,8 @@ static struct arm_smmu_device *arm_smmu_impl_probe(struct arm_smmu_device *smmu) ops = new_smmu->impl_ops;if (ops) { -/* get_viommu_size and vsmmu_init ops must be paired */ -if (WARN_ON (!ops->get_viommu_size != !ops->vsmmu_init)) { +/* get_viommu_ops and vsmmu_init ops must be paired */ +if (WARN_ON (!ops->get_viommu_ops != !ops->vsmmu_init)) { ret = -EINVAL ; goto err_remove; }
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +9 -7
@@ -931,7 +931,8 @@ struct arm_smmu_impl_ops {*/ void *(*hw_info)(struct arm_smmu_device *smmu,u32 *length,enum iommu_hw_info_type *type); -size_t (*get_viommu_size)(enum iommu_viommu_type viommu_type); +const struct iommufd_viommu_ops *(*get_viommu_ops)( +enum iommu_viommu_type viommu_type);int (*vsmmu_init)(struct arm_vsmmu *vsmmu,const struct iommu_user_data *user_data); };
@@ -1321,11 +1322,12 @@ struct arm_vsmmu {#if IS_ENABLED (CONFIG_ARM_SMMU_V3_IOMMUFD )void *arm_smmu_hw_info (struct device *dev,u32 *length,enum iommu_hw_info_type *type); -size_t arm_smmu_get_viommu_size (struct device *dev, -enum iommu_viommu_type viommu_type); -int arm_vsmmu_init (struct iommufd_viommu *viommu, -struct iommu_domain *parent_domain, -const struct iommu_user_data *user_data); +const struct iommufd_viommu_ops * +arm_smmu_get_viommu_ops (struct device *dev, +enum iommu_viommu_type viommu_type); +int arm_vsmmu_init (struct iommufd_viommu *viommu,struct device *dev, +struct iommu_domain *parent_domain, +const struct iommu_user_data *user_data);int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state,struct arm_smmu_nested_domain *nested_domain);void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state);
@@ -1337,7 +1339,7 @@ arm_vsmmu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,int arm_vsmmu_cache_invalidate (struct iommufd_viommu *viommu,struct iommu_user_data_array *array);#else -#define arm_smmu_get_viommu_size NULL +#define arm_smmu_get_viommu_ops NULL#define arm_smmu_hw_info NULL#define arm_vsmmu_init NULL#define arm_vsmmu_alloc_domain_nested NULL
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +17 -7
@@ -892,11 +892,14 @@ static void *tegra241_cmdqv_hw_info(struct arm_smmu_device *smmu, u32 *length,return info; } -static size_t tegra241_cmdqv_get_vintf_size (enum iommu_viommu_type viommu_type) +static const struct iommufd_viommu_ops tegra241_cmdqv_viommu_ops; + +static const struct iommufd_viommu_ops * +tegra241_cmdqv_get_viommu_ops (enum iommu_viommu_type viommu_type) {if (viommu_type !=IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV ) -return 0 ; - return VIOMMU_STRUCT_SIZE(struct tegra241_vintf, vsmmu.core); +return NULL ; +return &tegra241_cmdqv_viommu_ops; }static struct arm_smmu_impl_ops tegra241_cmdqv_impl_ops = {
@@ -907,7 +910,7 @@ static struct arm_smmu_impl_ops tegra241_cmdqv_impl_ops = { .device_remove = tegra241_cmdqv_remove, /* For user-space use */ .hw_info = tegra241_cmdqv_hw_info, - .get_viommu_size = tegra241_cmdqv_get_vintf_size, + .get_viommu_ops = tegra241_cmdqv_get_viommu_ops, .vsmmu_init = tegra241_cmdqv_init_vintf_user, };
@@ -1289,7 +1292,15 @@ static int tegra241_vintf_init_vsid(struct iommufd_vdevice *vdev)return 0 ; } -static struct iommufd_viommu_ops tegra241_cmdqv_viommu_ops = { +static size_t tegra241_cmdqv_get_vintf_size (struct device *dev, +enum iommu_viommu_type viommu_type) +{ + return VIOMMU_STRUCT_SIZE(struct tegra241_vintf, vsmmu.core); +} + +static const struct iommufd_viommu_ops tegra241_cmdqv_viommu_ops = { + .get_viommu_size = tegra241_cmdqv_get_vintf_size, + .viommu_init = arm_vsmmu_init, .destroy = tegra241_cmdqv_destroy_vintf_user, .alloc_domain_nested = arm_vsmmu_alloc_domain_nested,/* Non-accelerated commands will be still handled by the kernel */
@@ -1312,7 +1323,7 @@ tegra241_cmdqv_init_vintf_user(struct arm_vsmmu *vsmmu,int ret;/* -* Unsupported type should be rejected by tegra241_cmdqv_get_vintf_size. +* Unsupported type should be rejected by tegra241_cmdqv_get_viommu_ops. * Seeing one here indicates a kernel bug or some data corruption. */ if (WARN_ON (vsmmu->core.type !=IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV ))
@@ -1364,7 +1375,6 @@ tegra241_cmdqv_init_vintf_user(struct arm_vsmmu *vsmmu,dev_dbg (cmdqv->dev,"VINTF%u: allocated with vmid (%d)\n" , vintf->idx, vintf->vsmmu.vmid); - vsmmu->core.ops = &tegra241_cmdqv_viommu_ops;return 0 ; free_mmap:
drivers/iommu/iommufd/selftest.c +21 -10
@@ -767,7 +767,19 @@ static int mock_hw_queue_init_phys(struct iommufd_hw_queue *hw_queue, u32 index,return rc; } -static struct iommufd_viommu_ops mock_viommu_ops = { +static int mock_viommu_init (struct iommufd_viommu *viommu,struct device *dev, +struct iommu_domain *parent_domain, +const struct iommu_user_data *user_data); + +static size_t mock_get_viommu_size (struct device *dev, +enum iommu_viommu_type viommu_type) +{ + return VIOMMU_STRUCT_SIZE(struct mock_viommu, core); +} + +static const struct iommufd_viommu_ops mock_viommu_ops = { + .get_viommu_size = mock_get_viommu_size, + .viommu_init = mock_viommu_init, .destroy = mock_viommu_destroy, .alloc_domain_nested = mock_viommu_alloc_domain_nested, .cache_invalidate = mock_viommu_cache_invalidate,
@@ -775,17 +787,18 @@ static struct iommufd_viommu_ops mock_viommu_ops = { .hw_queue_init_phys = mock_hw_queue_init_phys, }; -static size_t mock_get_viommu_size (struct device *dev, -enum iommu_viommu_type viommu_type) +static const struct iommufd_viommu_ops * +mock_get_viommu_ops (struct device *dev, +enum iommu_viommu_type viommu_type) {if (viommu_type !=IOMMU_VIOMMU_TYPE_SELFTEST ) -return 0 ; - return VIOMMU_STRUCT_SIZE(struct mock_viommu, core); +return NULL ; +return &mock_viommu_ops; }static int mock_viommu_init (struct iommufd_viommu *viommu, -struct iommu_domain *parent_domain, -const struct iommu_user_data *user_data) +struct device *dev,struct iommu_domain *parent_domain, +const struct iommu_user_data *user_data) {struct mock_iommu_device *mock_iommu =container_of ( viommu->iommu_dev, struct mock_iommu_device, iommu_dev);
@@ -827,7 +840,6 @@ static int mock_viommu_init(struct iommufd_viommu *viommu,mutex_init (&mock_viommu->queue_mutex); mock_viommu->s2_parent =to_mock_domain (parent_domain); - viommu->ops = &mock_viommu_ops;return 0 ; err_destroy_mmap:
@@ -854,8 +866,7 @@ static const struct iommu_ops mock_ops = { .probe_device = mock_probe_device, .page_response = mock_domain_page_response, .user_pasid_table = true, - .get_viommu_size = mock_get_viommu_size, - .viommu_init = mock_viommu_init, + .get_viommu_ops = mock_get_viommu_ops, };static void mock_domain_free_nested (struct iommu_domain *domain)
drivers/iommu/iommufd/viommu.c +15 -17
@@ -33,7 +33,7 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd)struct iommufd_viommu *viommu;struct iommufd_device *idev;struct iommu_device *iommu_dev; -const struct iommu_ops *ops; +const struct iommufd_viommu_ops *ops;size_t viommu_size;int rc;
@@ -49,23 +49,25 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) rc = -EOPNOTSUPP ; goto out_put_idev; } - ops = iommu_dev->ops; -if (!ops->get_viommu_size || !ops->viommu_init) { +if (!iommu_dev->ops->get_viommu_ops) { rc = -EOPNOTSUPP ; goto out_put_idev; } - - viommu_size = ops->get_viommu_size (idev->dev, cmd->type); -if (!viommu_size) { + ops = iommu_dev->ops->get_viommu_ops (idev->dev, cmd->type); +if (!ops) { rc = -EOPNOTSUPP ; goto out_put_idev; } -/* -* It is a driver bug for providing a viommu_size smaller than the core -* vIOMMU structure size +* It is a driver bug to omit the required operations or provide a size +* smaller than the core vIOMMU structure. */ -if (WARN_ON_ONCE (viommu_size <sizeof (*viommu))) { +if (WARN_ON_ONCE (!ops->get_viommu_size || !ops->viommu_init)) { + rc = -EOPNOTSUPP ; + goto out_put_idev; + } + viommu_size = ops->get_viommu_size (idev->dev, cmd->type); +if (!viommu_size ||WARN_ON_ONCE (viommu_size <sizeof (*viommu))) { rc = -EOPNOTSUPP ; goto out_put_idev; }
@@ -104,16 +106,12 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd)*/ viommu->iommu_dev = iommu_dev; - rc = ops->viommu_init (viommu, hwpt_paging->common.domain, + rc = ops->viommu_init (viommu, idev->dev, + hwpt_paging->common.domain, user_data.len ? &user_data :NULL );if (rc) goto out_put_hwpt; - -/* It is a driver bug that viommu->ops isn't filled */ -if (WARN_ON_ONCE (!viommu->ops)) { - rc = -EOPNOTSUPP ; - goto out_put_hwpt; - } + viommu->ops = ops; cmd->out_viommu_id = viommu->obj.id; rc =iommufd_ucmd_respond (ucmd,sizeof (*cmd));
include/linux/iommu.h +5 -15
@@ -46,6 +46,7 @@ struct iommu_dma_msi_cookie;struct iommu_fault_param ;struct iommufd_ctx ;struct iommufd_viommu ; +struct iommufd_viommu_ops ;struct msi_desc ;struct msi_msg ;
@@ -669,16 +670,8 @@ __iommu_copy_struct_to_user(const struct iommu_user_data *dst_data,* - IOMMU_DOMAIN_DMA: must use a dma domain * - 0: use the default setting * @default_domain_ops: the default ops for domains -* @get_viommu_size: Get the size of a driver-level vIOMMU structure for a given -* @dev corresponding to @viommu_type. Driver should return 0 -* if vIOMMU isn't supported accordingly. It is required for -* driver to use the VIOMMU_STRUCT_SIZE macro to sanitize the -* driver-level vIOMMU structure related to the core one -* @viommu_init: Init the driver-level struct of an iommufd_viommu on a physical -* IOMMU instance @viommu->iommu_dev, as the set of virtualization -* resources shared/passed to user space IOMMU instance. Associate -* it with a nesting @parent_domain. It is required for driver to -* set @viommu->ops pointing to its own viommu_ops +* @get_viommu_ops: Return the vIOMMU operations supported by @dev for a type. +* Return NULL if the type is unsupported. * @owner: Driver module providing these ops * @identity_domain: An always available, always attachable identity * translation.
@@ -729,11 +722,8 @@ struct iommu_ops {int (*def_domain_type)(struct device *dev); -size_t (*get_viommu_size)(struct device *dev, -enum iommu_viommu_type viommu_type); -int (*viommu_init)(struct iommufd_viommu *viommu, -struct iommu_domain *parent_domain, -const struct iommu_user_data *user_data); +const struct iommufd_viommu_ops *(*get_viommu_ops)( +struct device *dev,enum iommu_viommu_type viommu_type);const struct iommu_domain_ops *default_domain_ops;struct module *owner;
include/linux/iommufd.h +14 -0
@@ -148,6 +148,15 @@ struct iommufd_hw_queue {/** * struct iommufd_viommu_ops - vIOMMU specific operations +* @get_viommu_size: Get the size of a driver-level vIOMMU structure for a given +* @dev corresponding to @viommu_type. Driver should return 0 +* if vIOMMU isn't supported accordingly. It is required for +* driver to use the VIOMMU_STRUCT_SIZE macro to sanitize the +* driver-level vIOMMU structure related to the core one +* @viommu_init: Init the driver-level struct of an iommufd_viommu on a physical +* IOMMU instance @viommu->iommu_dev, as the set of virtualization +* resources shared/passed to user space IOMMU instance. Associate +* it with a nesting @parent_domain. * @destroy: Clean up all driver-specific parts of an iommufd_viommu. The memory * of the vIOMMU will be free-ed by iommufd core after calling this op * @alloc_domain_nested: Allocate a IOMMU_DOMAIN_NESTED on a vIOMMU that holds a
@@ -190,6 +199,11 @@ struct iommufd_hw_queue {* does, it should set it to the @hw_queue->destroy pointer */ struct iommufd_viommu_ops { +size_t (*get_viommu_size)(struct device *dev, +enum iommu_viommu_type type); +int (*viommu_init)(struct iommufd_viommu *viommu,struct device *dev, +struct iommu_domain *parent_domain, +const struct iommu_user_data *user_data);void (*destroy)(struct iommufd_viommu *viommu); struct iommu_domain *(*alloc_domain_nested)( struct iommufd_viommu *viommu, u32 flags,
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 11/15] iommufd/viommu: Select vIOMMU operations before allocation
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] iommu/amd: unreferenced amd_viommu_ops after amd_iommufd_get_viommu_ops() --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] iommu/amd: unreferenced amd_viommu_ops after amd_iommufd_get_viommu_ops() --
commit 7d905a9d1ce6388259ae73f7d0cf19d414cd5da4
Author: Aneesh Kumar K.V (Arm) <[email protected]>
iommufd/viommu: Select vIOMMU operations before allocation
This commit adds iommu_ops::get_viommu_ops() to select vIOMMU operations before allocation and moves get_viommu_size() and viommu_init() into struct iommufd_viommu_ops across the IOMMUFD core and backend drivers.
Quoted 61 lines
drivers/iommu/amd/iommufd.c +13
-5@@ -32,13 +32,21 @@ void *amd_iommufd_hw_info(struct device *dev, u32 *length, enum iommu_hw_info_tyreturn hwinfo; } -size_t amd_iommufd_get_viommu_size (struct device *dev,enum iommu_viommu_type viommu_type) +static size_t amd_iommufd_get_viommu_size (struct device *dev, +enum iommu_viommu_type viommu_type) { return VIOMMU_STRUCT_SIZE(struct amd_iommu_viommu, core); } -int amd_iommufd_viommu_init (struct iommufd_viommu *viommu,struct iommu_domain *parent, -const struct iommu_user_data *user_data) +const struct iommufd_viommu_ops * +amd_iommufd_get_viommu_ops (struct device *dev,enum iommu_viommu_type viommu_type) +{ +return NULL ; +} + +int amd_iommufd_viommu_init (struct iommufd_viommu *viommu, +struct device *dev,struct iommu_domain *parent, +const struct iommu_user_data *user_data) {unsigned long flags;struct protection_domain *pdom =to_pdomain (parent);@@ -47,8 +55,6 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *xa_init_flags (&aviommu->gdomid_array,XA_FLAGS_ALLOC1 ); aviommu->parent = pdom; - viommu->ops = &amd_viommu_ops; -spin_lock_irqsave (&pdom->lock, flags);list_add (&aviommu->pdom_list, &pdom->viommu_list);spin_unlock_irqrestore (&pdom->lock, flags);@@ -73,5 +79,7 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)* struct iommufd_viommu_ops - vIOMMU specific operations */ static const struct iommufd_viommu_ops amd_viommu_ops = { + .get_viommu_size = amd_iommufd_get_viommu_size, + .viommu_init = amd_iommufd_viommu_init, .destroy = amd_iommufd_viommu_destroy, };drivers/iommu/amd/iommufd.h +4
-3@@ -8,13 +8,16 @@#if IS_ENABLED (CONFIG_AMD_IOMMU_IOMMUFD )void *amd_iommufd_hw_info (struct device *dev,u32 *length,enum iommu_hw_info_type *type); -size_t amd_iommufd_get_viommu_size (struct device *dev,enum iommu_viommu_type viommu_type); -int amd_iommufd_viommu_init (struct iommufd_viommu *viommu,struct iommu_domain *parent, -const struct iommu_user_data *user_data); +const struct iommufd_viommu_ops * +amd_iommufd_get_viommu_ops (struct device *dev, +enum iommu_viommu_type viommu_type); +int amd_iommufd_viommu_init(struct iommufd_viommu *viommu,
drivers/iommu/amd/iommufd.h line 23
Quoted 10 lines
drivers/iommu/amd/iommufd.h +3
-1+ struct device *dev, struct iommu_domain *parent, + const struct iommu_user_data *user_data);#else #define amd_iommufd_hw_info NULL#define amd_iommufd_viommu_init NULL -#define amd_iommufd_get_viommu_size NULL +#define amd_iommufd_get_viommu_ops NULL#endif /* CONFIG_AMD_IOMMU_IOMMUFD */ #endif /* AMD_IOMMUFD_H */
[Severity: Low]
With viommu->ops = &amd_viommu_ops removed from amd_iommufd_viommu_init() and amd_iommufd_get_viommu_ops() unconditionally returning NULL, amd_viommu_ops in drivers/iommu/amd/iommufd.c has no remaining references.
When building with CONFIG_AMD_IOMMU_IOMMUFD=y, won't this trigger -Wunused-const-variable warnings on amd_viommu_ops under W=1 (and Clang -Wunused-function warnings on its static callbacks)?
Also, since amd_iommufd_viommu_init() is no longer referenced from drivers/iommu/amd/iommu.c, should it be made static in drivers/iommu/amd/iommufd.c and removed from drivers/iommu/amd/iommufd.h, with amd_viommu_ops marked __maybe_unused while AMD vIOMMU allocation is disabled?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=11
kernel test robot
build botRe: [PATCH v8 11/15] iommufd/viommu: Select vIOMMU operations before allocation
In reply to Aneesh Kumar K.V (Arm)kernel test robot noticed the following build warnings:
Hi Aneesh,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4]
url: https://github.com/intel-lab-lkp/linux/commits/Aneesh-Kumar-K-V-Arm/PCI-TSM-Move-core-code-into-its-own-directory/20261104-214618 base: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4 patch link: https://lore.kernel.org/r/20261104161632.3547423-12-aneesh.kumar%40kernel.org patch subject: [PATCH v8 11/15] iommufd/viommu: Select vIOMMU operations before allocation config: x86_64-buildonly-randconfig-1001-20261105 (https://download.01.org/0day-ci/archive/20261105/[email protected]/config) compiler: gcc-14 (Debian 14.2.0-19) 14.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261105/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/iommu/amd/iommufd.c:81:40: warning: 'amd_viommu_ops' defined but not used [-Wunused-const-variable=]
81 | static const struct iommufd_viommu_ops amd_viommu_ops = {
| ^~~~~~~~~~~~~~vim +/amd_viommu_ops +81 drivers/iommu/amd/iommufd.c
757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 76 757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 77 /* 757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 78 * See include/linux/iommufd.h 757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 79 * struct iommufd_viommu_ops - vIOMMU specific operations 757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 80 */
757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 @81 static const struct iommufd_viommu_ops amd_viommu_ops = {
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
kernel test robot
build botRe: [PATCH v8 11/15] iommufd/viommu: Select vIOMMU operations before allocation
In reply to Aneesh Kumar K.V (Arm)kernel test robot noticed the following build warnings:
Hi Aneesh,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4]
url: https://github.com/intel-lab-lkp/linux/commits/Aneesh-Kumar-K-V-Arm/PCI-TSM-Move-core-code-into-its-own-directory/20261104-214618 base: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4 patch link: https://lore.kernel.org/r/20261104161632.3547423-12-aneesh.kumar%40kernel.org patch subject: [PATCH v8 11/15] iommufd/viommu: Select vIOMMU operations before allocation config: x86_64-allmodconfig (https://download.01.org/0day-ci/archive/20261105/[email protected]/config) compiler: clang version 22.1.8 (https://github.com/llvm/llvm-project ca7933e47d3a3451d81e72ac174dcb5aa28b59d1) rustc: rustc 1.96.0 (ac68faa20 2026-05-25) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261105/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/iommu/amd/iommufd.c:81:40: warning: unused variable 'amd_viommu_ops' [-Wunused-const-variable]
81 | static const struct iommufd_viommu_ops amd_viommu_ops = {
| ^~~~~~~~~~~~~~1 warning generated.
vim +/amd_viommu_ops +81 drivers/iommu/amd/iommufd.c
757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 76 757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 77 /* 757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 78 * See include/linux/iommufd.h 757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 79 * struct iommufd_viommu_ops - vIOMMU specific operations 757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 80 */
757d2b1fdf5b7d6 Suravee Suthikulpanit 2026-01-15 @81 static const struct iommufd_viommu_ops amd_viommu_ops = {
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Tian, Kevin
maintainerRE: [PATCH v8 11/15] iommufd/viommu: Select vIOMMU operations before allocation
In reply to Aneesh Kumar K.V (Arm)Quoted 13 lines
From: Aneesh Kumar K.V (Arm) <[email protected]>
Sent: Thursday, November 5, 2026 12:16 AMPhysical IOMMU drivers currently provide the vIOMMU size and initialization callbacks through
iommu_ops. The initialization callback then selects and installs the correspondingiommufd_viommu_ops.Add
iommu_ops::get_viommu_ops()to select the operations from the physical device and requested vIOMMU type before allocation. Move the size and initialization callbacks intoiommufd_viommu_ops. The core can then validate the selected operations, allocate the driver structure, initialize it, and install the operations.
Quoted 6 lines
Convert AMD, Arm SMMUv3, Tegra CMDQV, and the selftest backend to the new interface.
Selecting the operations early allows subsequent changes to use implementation-specific vIOMMU properties, including when validating the parent HWPT.
what does "implementation-specific" mean? Isn't every existing vIOMMU type already implementation specific?
Quoted 10 lines
/*
* This function is assigned to structiommufd_viommu_ops.alloc_domain_nested()
- * during the call to structiommu_ops.viommu_init().
+ * when the vIOMMU operations are selected.
*/
structiommu_domain* amd_iommu_alloc_domain_nested(structiommufd_viommu*viommu, u32
flags,
the comment sounds changing the semantics.
ideally it's still assigned at viommu_init(), just from iommufd_viommu_ops instead of iommu_ops now.
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 12/15 iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations
In reply to Aneesh Kumar K.V (Arm)In confidential-computing configurations, a vIOMMU may be implemented by a TEE Security Manager rather than by the physical IOMMU driver. Such an implementation also needs the TSM identity when creating and validating vdevices.
Allow VIOMMU_ALLOC to first query the TSM associated with the target PCI device for vIOMMU operations. Fall back to the physical IOMMU driver when the TSM does not provide operations for the requested vIOMMU type.
Resolve the PCI TSM association under pci_tsm_rwsem, pin the module providing the operations, and take a TSM device reference. Store the TSM device in the vIOMMU and release both references on allocation failure, fallback to the physical IOMMU driver, or vIOMMU destruction. Keep the module pinned until after dropping the device reference, since the final put may release the TSM device.
The device reference preserves the TSM allocation. Backends providing vIOMMU operations must not unregister their TSM while the module is pinned, since the reference does not prevent backend resource teardown.
This enables the Realm vIOMMU implementation to provide its operations through the PCI TSM backend and retain the TSM identity needed by its vdevices.
--- drivers/iommu/iommufd/viommu.c | 35 +++++++++++++++++++++++++++----- drivers/pci/tsm/core.c | 22 ++++++++++++++++++++ drivers/virt/coco/tsm-core.c | 37 ++++++++++++++++++++++++++++++++++ include/linux/iommufd.h | 2 ++ include/linux/pci-tsm.h | 24 ++++++++++++++++++++++ include/linux/tsm.h | 25 +++++++++++++++++++++++ 6 files changed, 140 insertions(+), 5 deletions(-)
drivers/iommu/iommufd/viommu.c +30 -5
@@ -15,6 +15,8 @@ void iommufd_viommu_destroy(struct iommufd_object *obj)if (viommu->ops && viommu->ops->destroy) viommu->ops->destroy (viommu); +if (viommu->tsm_dev) +tsm_put_device (viommu->tsm_dev);refcount_dec (&viommu->hwpt->common.obj.users);if (viommu->kvm_file)fput (viommu->kvm_file);
@@ -30,6 +32,7 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) .len = cmd->data_len, };struct iommufd_hwpt_paging *hwpt_paging; +struct tsm_dev *tsm_dev =NULL ;struct iommufd_viommu *viommu;struct iommufd_device *idev;struct iommu_device *iommu_dev;
@@ -49,15 +52,33 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) rc = -EOPNOTSUPP ; goto out_put_idev; } -if (!iommu_dev->ops->get_viommu_ops) { - rc = -EOPNOTSUPP ; + tsm_dev =tsm_get_device (idev->dev); +if (IS_ERR (tsm_dev)) { + rc =PTR_ERR (tsm_dev); + tsm_dev =NULL ; goto out_put_idev; } - ops = iommu_dev->ops->get_viommu_ops (idev->dev, cmd->type); -if (!ops) { - rc = -EOPNOTSUPP ; + ops = tsm_dev ?tsm_viommu_get_ops (tsm_dev, idev->dev, cmd->type) :NULL ; +if (IS_ERR (ops)) { + rc =PTR_ERR (ops); goto out_put_idev; } +if (!ops) { +if (tsm_dev) { +tsm_put_device (tsm_dev); + tsm_dev =NULL ; + } +if (!iommu_dev->ops->get_viommu_ops) { + rc = -EOPNOTSUPP ; + goto out_put_idev; + } + ops = iommu_dev->ops->get_viommu_ops (idev->dev, cmd->type); +if (!ops) { + rc = -EOPNOTSUPP ; + goto out_put_idev; + } + } +/* * It is a driver bug to omit the required operations or provide a size * smaller than the core vIOMMU structure.
@@ -95,6 +116,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) viommu->kvm_file =get_file (idev->kvm_file); viommu->type = cmd->type; viommu->ictx = ucmd->ictx; + viommu->tsm_dev = tsm_dev; + tsm_dev =NULL ; viommu->hwpt = hwpt_paging;refcount_inc (&viommu->hwpt->common.obj.users);INIT_LIST_HEAD (&viommu->veventqs);
@@ -119,6 +142,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) out_put_hwpt:iommufd_put_object (ucmd->ictx, &hwpt_paging->common.obj); out_put_idev: +if (tsm_dev) +tsm_put_device (tsm_dev);iommufd_put_object (ucmd->ictx, &idev->obj);return rc; }
drivers/pci/tsm/core.c +22 -0
@@ -9,6 +9,8 @@#define dev_fmt (fmt) "PCI/TSM: " fmt#include <linux/bitfield.h> +#include <linux/iommufd.h> +#include <linux/module.h> #include <linux/pci.h> #include <linux/pci-doe.h> #include <linux/pci-tsm.h>
@@ -36,6 +38,26 @@ static const struct pci_tsm_ops *to_pci_tsm_ops(struct pci_tsm *tsm)return tsm->tsm_dev->pci_ops; } +struct tsm_dev *pci_tsm_get_device (struct pci_dev *pdev) +{ +const struct pci_tsm_ops *ops; +struct tsm_dev *tsm_dev; + +guard (rwsem_read)(&pci_tsm_rwsem); +if (!pdev->tsm) +return NULL ; + + tsm_dev = pdev->tsm->tsm_dev; + ops = tsm_dev->pci_ops; +if (!ops->viommu_get_ops) +return NULL ; +if (!try_module_get (ops->owner)) +return ERR_PTR (-ENODEV ); +get_device (&tsm_dev->dev); +return tsm_dev; +} +EXPORT_SYMBOL_GPL (pci_tsm_get_device); +static inline bool is_dsm (struct pci_dev *pdev) {return pdev->tsm && pdev->tsm->dsm_dev == pdev;
drivers/virt/coco/tsm-core.c +37 -0
@@ -9,6 +9,16 @@#include <linux/cleanup.h> #include <linux/pci-tsm.h> +/* The caller must hold a tsm_get_device() reference for the entire use. */ +const struct iommufd_viommu_ops *tsm_viommu_get_ops (struct tsm_dev *tsm_dev, +struct device *dev,enum iommu_viommu_type type) +{ +if (!tsm_dev->pci_ops->viommu_get_ops) +return NULL ; +return tsm_dev->pci_ops->viommu_get_ops (dev, type); +} +EXPORT_SYMBOL_GPL (tsm_viommu_get_ops); +static void tsm_release (struct device *);static const struct class tsm_class = { .name ="tsm" ,
@@ -16,6 +26,33 @@ static const struct class tsm_class = { };static DEFINE_IDA (tsm_ida); +/** +* tsm_get_device() - Pin the TSM attached to a device +* @dev: device whose TSM is to be pinned +* +* A successful lookup takes a TSM device reference and pins the backend +* module. Release both with tsm_put_device(). Backends providing vIOMMU +* operations must not unregister their TSM independently of module unload. +* Return: NULL if no vIOMMU-capable TSM is attached, an error if the module +* cannot be pinned, or the TSM device associated with the pinned backend. +*/ +struct tsm_dev *tsm_get_device (struct device *dev) +{ +if (!dev_is_pci (dev)) +return NULL ; +return pci_tsm_get_device (to_pci_dev (dev)); +} +EXPORT_SYMBOL_GPL (tsm_get_device); + +void tsm_put_device (struct tsm_dev *tsm_dev) +{ +struct module *owner = tsm_dev->pci_ops->owner; + +put_device (&tsm_dev->dev); +module_put (owner); +} +EXPORT_SYMBOL_GPL (tsm_put_device); +static int match_id (struct device *dev,const void *data) { struct tsm_dev *tsm_dev = container_of(dev, struct tsm_dev, dev);
include/linux/iommufd.h +2 -0
@@ -23,6 +23,7 @@ struct iommufd_access;struct iommufd_ctx ;struct iommufd_device ;struct iommufd_viommu_ops ; +struct tsm_dev ;struct page ;enum iommufd_object_type {
@@ -104,6 +105,7 @@ struct iommufd_viommu {struct iommu_device *iommu_dev;struct iommufd_hwpt_paging *hwpt;struct file *kvm_file; +struct tsm_dev *tsm_dev;const struct iommufd_viommu_ops *ops;
include/linux/pci-tsm.h +24 -0
@@ -3,7 +3,10 @@#define __PCI_TSM_H#include <linux/mutex.h> #include <linux/pci.h> +#include <uapi/linux/iommufd.h> +struct iommufd_viommu_ops ; +struct module ;struct pci_tsm ;struct pci_tsm_context ;struct tsm_dev ;
@@ -16,12 +19,27 @@ struct tsm_dev;* @devsec_ops: Lock, unlock, and interrogate the security state of the * function via the platform TSM (typically virtual function * operations). +* @viommu_get_ops: Return operations for a supported TSM-owned vIOMMU type, +* or NULL if unsupported +* @owner: Module providing the vIOMMU operations, if built as a module +* +* The vIOMMU owner holds a TSM device reference and pins the backend module +* before querying @viommu_get_ops, releasing both after vIOMMU destruction. +* A backend providing this callback must not unregister its TSM while the +* module is pinned. * * This operations are mutually exclusive either a tsm_dev instance * manages physical link properties or it manages function security * states like TDISP lock/unlock. +* +* @viommu_get_ops runs with the backend module pinned. The PCI/TSM +* association is checked during acquisition under pci_tsm_rwsem. */ struct pci_tsm_ops { +struct module *owner; +const struct iommufd_viommu_ops *(*viommu_get_ops)( +struct device *dev,enum iommu_viommu_type type); +/* * struct pci_tsm_link_ops - Manage physical link and the TSM/DSM session * @probe: establish context with the TSM (allocate / wrap 'struct
@@ -151,6 +169,7 @@ struct pci_tsm_pf0 *pci_tsm_context_pf0(struct pci_tsm_context *context);struct pci_dev *pci_tsm_context_dsm_dev (struct pci_tsm_context *context);bool pci_tsm_context_match_device (struct pci_tsm_context *context,struct pci_dev *pdev); +struct tsm_dev *pci_tsm_get_device (struct pci_dev *pdev);#else static inline int pci_tsm_register (struct tsm_dev *tsm_dev) {
@@ -165,6 +184,11 @@ static inline bool pci_tsm_is_configured(struct pci_dev *pdev)return false; } +static inline struct tsm_dev *pci_tsm_get_device (struct pci_dev *pdev) +{ +return NULL ; +} +static inline struct pci_tsm_context *pci_tsm_context_get (struct pci_dev *pdev,struct tsm_dev *viommu_tsm_dev) {
include/linux/tsm.h +25 -0
@@ -110,6 +110,8 @@ struct tsm_report_ops { };struct pci_tsm_ops ; +struct iommufd_viommu_ops ; +struct tsm_dev {struct device dev;int id;
@@ -124,4 +126,27 @@ int tsm_report_unregister(const struct tsm_report_ops *ops);struct tsm_dev *tsm_register (struct device *parent,struct pci_tsm_ops *ops);void tsm_unregister (struct tsm_dev *tsm_dev);struct tsm_dev *find_tsm_dev (int id); + +#ifdef CONFIG_TSM +struct tsm_dev *tsm_get_device (struct device *dev); +void tsm_put_device (struct tsm_dev *tsm_dev); +const struct iommufd_viommu_ops *tsm_viommu_get_ops (struct tsm_dev *tsm_dev, +struct device *dev,enum iommu_viommu_type type); +#else +static inline struct tsm_dev *tsm_get_device (struct device *dev) +{ +return NULL ; +} + +static inline void tsm_put_device (struct tsm_dev *tsm_dev) +{ +} + +static inline const struct iommufd_viommu_ops * +tsm_viommu_get_ops (struct tsm_dev *tsm_dev,struct device *dev, +enum iommu_viommu_type type) +{ +return NULL ; +} +#endif #endif /* __TSM_H */
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 12/15] iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations
Sashiko has reviewed this patch and found no issues.
Quoted 15 lines
In confidential-computing configurations, a vIOMMU may be implemented by a TEE Security Manager rather than by the physical IOMMU driver. Such an implementation also needs the TSM identity when creating and validating vdevices.
Allow
VIOMMU_ALLOCto first query the TSM associated with the target PCI device for vIOMMU operations. Fall back to the physical IOMMU driver when the TSM does not provide operations for the requested vIOMMU type.Resolve the PCI TSM association under
pci_tsm_rwsem, pin the module providing the operations, and take a TSM device reference. Store the TSM device in the vIOMMU and release both references on allocation failure, fallback to the physical IOMMU driver, or vIOMMU destruction. Keep the module pinned until after dropping the device reference, since the final put may release the TSM device.
Quoted 6 lines
[ ... ]Cc Robin Murphy <[email protected]>Cc Bjorn Helgaas <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=12
Re: [PATCH v8 12/15] iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations
In reply to Aneesh Kumar K.V (Arm)Hi Aneesh,
Quoted 66 lines · On Wed, 4 Nov 2026 21:46:29 +0530 "Aneesh Kumar K.V (Arm)"…
In confidential-computing configurations, a vIOMMU may be implemented by a TEE Security Manager rather than by the physical IOMMU driver. Such an implementation also needs the TSM identity when creating and validating vdevices.
Allow
VIOMMU_ALLOCto first query the TSM associated with the target PCI device for vIOMMU operations. Fall back to the physical IOMMU driver when the TSM does not provide operations for the requested vIOMMU type.Resolve the PCI TSM association under
pci_tsm_rwsem, pin the module providing the operations, and take a TSM device reference. Store the TSM device in the vIOMMU and release both references on allocation failure, fallback to the physical IOMMU driver, or vIOMMU destruction. Keep the module pinned until after dropping the device reference, since the final put may release the TSM device.The device reference preserves the TSM allocation. Backends providing vIOMMU operations must not unregister their TSM while the module is pinned, since the reference does not prevent backend resource teardown.
This enables the Realm vIOMMU implementation to provide its operations through the PCI TSM backend and retain the TSM identity needed by its vdevices.
Cc Jason Gunthorpe <[email protected]>Cc Kevin Tian <[email protected]>Cc Joerg Roedel <[email protected]>Cc Will Deacon <[email protected]>Cc Robin Murphy <[email protected]>Cc Bjorn Helgaas <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>--- drivers/iommu/iommufd/viommu.c | 35 +++++++++++++++++++++++++++----- drivers/pci/tsm/core.c | 22 ++++++++++++++++++++ drivers/virt/coco/tsm-core.c | 37 ++++++++++++++++++++++++++++++++++ include/linux/iommufd.h | 2 ++ include/linux/pci-tsm.h | 24 ++++++++++++++++++++++ include/linux/tsm.h | 25 +++++++++++++++++++++++ 6 files changed, 140 insertions(+), 5 deletions(-)(quoted diff) +0
-0
b/drivers/iommu/iommufd/viommu.cindex16e8b6421f1f..1686f19c5060100644 ---a/drivers/iommu/iommufd/viommu.c
+++b/drivers/iommu/iommufd/viommu.cdrivers/iommu/iommufd/viommu.c +3
-0@@ -15,6 +15,8 @@ void iommufd_viommu_destroy(struct iommufd_object *obj)if (viommu->ops && viommu->ops->destroy) viommu->ops->destroy (viommu); +if (viommu->tsm_dev) +tsm_put_device (viommu->tsm_dev);refcount_dec (&viommu->hwpt->common.obj.users);if (viommu->kvm_file)fput (viommu->kvm_file);@@ -30,6 +32,7 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) .len = cmd->data_len, };struct iommufd_hwpt_paging *hwpt_paging; +struct tsm_dev *tsm_dev =NULL ;struct iommufd_viommu *viommu;struct iommufd_device *idev;struct iommu_device *iommu_dev;
drivers/iommu/iommufd/viommu.c int iommufd_viommu_alloc_ioctl(struct · line 55
Quoted 6 lines
drivers/iommu/iommufd/viommu.c +1
-2@@ -49,15 +52,33 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) rc = -EOPNOTSUPP; goto out_put_idev; } -if (!iommu_dev->ops->get_viommu_ops) { - rc = -EOPNOTSUPP ; + tsm_dev =tsm_get_device (idev->dev);
So I want to reuse this flow for MSHV since tsm already does module pinning and locking for backend providers. Once MSHV supports TDISP, it could register a link TSM similarly to the CCA cca_link_pci_ops.
However, we also want to use the same viommu/vdevice flow to support non-confidential device assignment with Hypervisor managed S2 mapping. I belive pKVM has the same need.[1]
I am thinking MSHV/pKVM can register an assignment-only tsm_dev to leverage this? Could PCI/TSM support this role without device-security?
Something like:
@@ -48,7 +41,13 @@ struct pci_tsm_ops {
struct module *owner;
const struct iommufd_viommu_ops *(*viommu_get_ops)(
struct device *dev, enum iommu_viommu_type type);+
+ struct pci_tsm_assignment_ops {
+ bool (*match)(struct pci_dev *pdev);
+ struct pci_tsm *(*probe)(struct tsm_dev *tsm_dev,
+ struct pci_dev *pdev);
+ void (*remove)(struct pci_tsm *tsm);
+ } assignment_ops;
Quoted 287 lines
drivers/iommu/iommufd/viommu.c +22
-3+if (IS_ERR (tsm_dev)) { + rc =PTR_ERR (tsm_dev); + tsm_dev =NULL ; goto out_put_idev; } - ops = iommu_dev->ops->get_viommu_ops (idev->dev, cmd->type); -if (!ops) { - rc = -EOPNOTSUPP ; + ops = tsm_dev ?tsm_viommu_get_ops (tsm_dev, idev->dev, cmd->type) :NULL ; +if (IS_ERR (ops)) { + rc =PTR_ERR (ops); goto out_put_idev; } +if (!ops) { +if (tsm_dev) { +tsm_put_device (tsm_dev); + tsm_dev =NULL ; + } +if (!iommu_dev->ops->get_viommu_ops) { + rc = -EOPNOTSUPP ; + goto out_put_idev; + } + ops = iommu_dev->ops->get_viommu_ops (idev->dev, cmd->type); +if (!ops) { + rc = -EOPNOTSUPP ; + goto out_put_idev; + } + } +/* * It is a driver bug to omit the required operations or
provide a sizedrivers/iommu/iommufd/viommu.c +2
-0* smaller than the core vIOMMU structure. @@ -95,6 +116,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) viommu->kvm_file = get_file(idev->kvm_file); viommu->type = cmd->type; viommu->ictx = ucmd->ictx; + viommu->tsm_dev = tsm_dev; + tsm_dev =NULL ; viommu->hwpt = hwpt_paging;refcount_inc (&viommu->hwpt->common.obj.users);INIT_LIST_HEAD (&viommu->veventqs);@@ -119,6 +142,8 @@ int iommufd_viommu_alloc_ioctl(struct
iommufd_ucmd*ucmd)out_put_hwpt:drivers/iommu/iommufd/viommu.c +2
-0iommufd_put_object (ucmd->ictx, &hwpt_paging->common.obj); out_put_idev: +if (tsm_dev) +tsm_put_device (tsm_dev);iommufd_put_object (ucmd->ictx, &idev->obj);return rc; }drivers/pci/tsm/core.c +22
-0@@ -9,6 +9,8 @@#define dev_fmt (fmt) "PCI/TSM: " fmt#include <linux/bitfield.h> +#include <linux/iommufd.h> +#include <linux/module.h> #include <linux/pci.h> #include <linux/pci-doe.h> #include <linux/pci-tsm.h> @@ -36,6 +38,26 @@ static const struct pci_tsm_ops *to_pci_tsm_ops(struct pci_tsm *tsm) return tsm->tsm_dev->pci_ops; } +struct tsm_dev *pci_tsm_get_device (struct pci_dev *pdev) +{ +const struct pci_tsm_ops *ops; +struct tsm_dev *tsm_dev; + +guard (rwsem_read)(&pci_tsm_rwsem); +if (!pdev->tsm) +return NULL ; + + tsm_dev = pdev->tsm->tsm_dev; + ops = tsm_dev->pci_ops; +if (!ops->viommu_get_ops) +return NULL ; +if (!try_module_get (ops->owner)) +return ERR_PTR (-ENODEV ); +get_device (&tsm_dev->dev); +return tsm_dev; +} +EXPORT_SYMBOL_GPL (pci_tsm_get_device); +static inline bool is_dsm (struct pci_dev *pdev) {return pdev->tsm && pdev->tsm->dsm_dev == pdev;(quoted diff) +0
-0
b/drivers/virt/coco/tsm-core.cindexe7807dc94807..92ce3337f1ea100644drivers/virt/coco/tsm-core.c +1
-0@@ -9,6 +9,16 @@#include <linux/cleanup.h> #include <linux/pci-tsm.h> +/* The caller must hold a tsm_get_device() reference for the entire use. */ +const struct
iommufd_viommu_ops*tsm_viommu_get_ops(structtsm_dev*tsm_dev,drivers/virt/coco/tsm-core.c +13
-0+ struct device *dev, enum iommu_viommu_type type) +{ +if (!tsm_dev->pci_ops->viommu_get_ops) +return NULL ; +return tsm_dev->pci_ops->viommu_get_ops (dev, type); +} +EXPORT_SYMBOL_GPL (tsm_viommu_get_ops); +static void tsm_release (struct device *);static const struct class tsm_class = { .name ="tsm" ,@@ -16,6 +26,33 @@ static const struct class tsm_class = { };static DEFINE_IDA (tsm_ida); +/** +* tsm_get_device() - Pin the TSM attached to a device +* @dev: device whose TSM is to be pinned +* +* A successful lookup takes a TSM device reference and pins the backend
+ * module. Release both withtsm_put_device(). Backends providing vIOMMU
+ * operations must not unregister their TSM independently of module unload.
+ * Return: NULL if no vIOMMU-capable TSM is attached, an error if the module
+ * cannot be pinned, or the TSM device associated with the pinned backend.
+ */drivers/virt/coco/tsm-core.c +17
-0+struct tsm_dev *tsm_get_device (struct device *dev) +{ +if (!dev_is_pci (dev)) +return NULL ; +return pci_tsm_get_device (to_pci_dev (dev)); +} +EXPORT_SYMBOL_GPL (tsm_get_device); + +void tsm_put_device (struct tsm_dev *tsm_dev) +{ +struct module *owner = tsm_dev->pci_ops->owner; + +put_device (&tsm_dev->dev); +module_put (owner); +} +EXPORT_SYMBOL_GPL (tsm_put_device); +static int match_id (struct device *dev,const void *data) { struct tsm_dev *tsm_dev = container_of(dev, struct tsm_dev,dev); diff --git
a/include/linux/iommufd.hb/include/linux/iommufd.hindex5f5c67b24134..a4b29915d0ca100644include/linux/iommufd.h +2
-0@@ -23,6 +23,7 @@ struct iommufd_access;struct iommufd_ctx ;struct iommufd_device ;struct iommufd_viommu_ops ; +struct tsm_dev ;struct page ;enum iommufd_object_type {@@ -104,6 +105,7 @@ struct iommufd_viommu {struct iommu_device *iommu_dev;struct iommufd_hwpt_paging *hwpt;struct file *kvm_file; +struct tsm_dev *tsm_dev;const struct iommufd_viommu_ops *ops;include/linux/pci-tsm.h +3
-0@@ -3,7 +3,10 @@#define __PCI_TSM_H#include <linux/mutex.h> #include <linux/pci.h> +#include <uapi/linux/iommufd.h> +struct iommufd_viommu_ops ; +struct module ;struct pci_tsm ;struct pci_tsm_context ;struct tsm_dev ;@@ -16,12 +19,27 @@ struct tsm_dev;* @devsec_ops: Lock, unlock, and interrogate the security state of the
* function via the platform TSM (typically virtual
function
* operations).
+ * @viommu_get_ops: Return operations for a supported TSM-owned vIOMMU type,
+ * or NULL if unsupported
+ * @owner: Module providing the vIOMMU operations, if built as a module
+ *
+ * The vIOMMU owner holds a TSM device reference and pins the backend module
+ * before querying @viommu_get_ops, releasing both after vIOMMU destruction.
+ * A backend providing this callback must not unregister its TSM while the
+ * module is pinned.
*
* This operations are mutually exclusive either atsm_devinstance
* manages physical link properties or it manages function security
* states like TDISP lock/unlock.
+ *
+ * @viommu_get_ops runs with the backend module pinned. The PCI/TSM
+ * association is checked during acquisition underpci_tsm_rwsem. */
structpci_tsm_ops{include/linux/pci-tsm.h +4
-0+struct module *owner; +const struct iommufd_viommu_ops *(*viommu_get_ops)( +struct device *dev,enum iommu_viommu_type type); +/* * struct pci_tsm_link_ops - Manage physical link and the TSM/DSM session
* @probe: establish context with the TSM (allocate / wrap
'struct @@ -151,6 +169,7 @@ structpci_tsm_pf0*pci_tsm_context_pf0(structpci_tsm_context*context); structpci_dev*pci_tsm_context_dsm_dev(structpci_tsm_context*context); boolpci_tsm_context_match_device(struct pci_tsm_context *context, struct pci_dev *pdev); +structtsm_dev*pci_tsm_get_device(structpci_dev*pdev); #else
static inline intpci_tsm_register(struct tsm_dev *tsm_dev){include/linux/pci-tsm.h +4
-0@@ -165,6 +184,11 @@ static inline bool pci_tsm_is_configured(struct pci_dev *pdev) return false; } +static inline struct tsm_dev *pci_tsm_get_device (struct pci_dev *pdev) +{ +return NULL ; +} +static inline struct pci_tsm_context * pci_tsm_context_get(struct pci_dev *pdev, struct tsm_dev
*viommu_tsm_dev) {include/linux/tsm.h +2
-0@@ -110,6 +110,8 @@ struct tsm_report_ops { };struct pci_tsm_ops ; +struct iommufd_viommu_ops ; +struct tsm_dev {struct device dev;int id;@@ -124,4 +126,27 @@ int tsm_report_unregister(const struct
tsm_report_ops*ops); structtsm_dev*tsm_register(struct device *parent, structpci_tsm_ops*ops); voidtsm_unregister(struct tsm_dev *tsm_dev); structtsm_dev*find_tsm_dev(int id);include/linux/tsm.h +5
-0+ +#ifdef CONFIG_TSM +struct tsm_dev *tsm_get_device (struct device *dev); +void tsm_put_device (struct tsm_dev *tsm_dev); +const struct iommufd_viommu_ops *tsm_viommu_get_ops(struct tsm_dev
*tsm_dev,include/linux/tsm.h +18
-0+ struct device *dev, enum iommu_viommu_type type); +#else +static inline struct tsm_dev *tsm_get_device (struct device *dev) +{ +return NULL ; +} + +static inline void tsm_put_device (struct tsm_dev *tsm_dev) +{ +} + +static inline const struct iommufd_viommu_ops * +tsm_viommu_get_ops (struct tsm_dev *tsm_dev,struct device *dev, +enum iommu_viommu_type type) +{ +return NULL ; +} +#endif #endif /* __TSM_H */
Re: [PATCH v8 12/15] iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations
Quoted 2 lines · Jacob Pan
I am thinking MSHV/pKVM can register an assignment-only
tsm_devto leverage this? Could PCI/TSM support this role without device-security?
It is certainly not what TSM was originally imagined for, but with our new arrangement, yah maybe so?
TSM basically just becomes this weird overlay driver on the PCI device that does stuff. Some of the stuff it might do:
- Register an attestation subsystem device to do SPDM
- Register to allow iommufd to create a viommu
- Some status sysfs ?
- ??
It all seems separable and composable to me, so if you don't want to launch an attestation subsystem device because there is no SPDM then I think that might be OK?
Another option would be to somehow obtain the viommu from the kvm file descriptor and rely on the fd to handle the module pinning/etc. I don't know how to do that nicely with the way the modules work though..
Jason
Re: [PATCH v8 12/15] iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations
Hi Jason,
Quoted 8 lines · On Wed, 4 Nov 2026 20:49:34 -0400 Jason Gunthorpe <[email protected]> wrote:
Quoted 3 lines · Jacob Pan
I am thinking MSHV/pKVM can register an assignment-only
tsm_devto leverage this? Could PCI/TSM support this role without device-security?It is certainly not what TSM was originally imagined for, but with our new arrangement, yah maybe so?
I will rebase on this set and send out patches for a detailed review.
Quoted 7 lines
TSM basically just becomes this weird overlay driver on the PCI device that does stuff. Some of the stuff it might do:
- Register an attestation subsystem device to do SPDM
- Register to allow iommufd to create a viommu
- Some status sysfs ?
- ??
So we may expand pci tsm to have more than one role? currently it has one.
int pci_tsm_register(struct tsm_dev *tsm_dev) {
...
roles = is_link_tsm(tsm_dev) + is_devsec_tsm(tsm_dev) + is_assignment_tsm(tsm_dev); if (roles != 1) return -EINVAL;
Quoted 8 lines
It all seems separable and composable to me, so if you don't want to launch an attestation subsystem device because there is no SPDM then I think that might be OK?
Another option would be to somehow obtain the viommu from the kvm file descriptor and rely on the fd to handle the module pinning/etc. I don't know how to do that nicely with the way the modules work though..
I did this in mshv-iommufd RFC to associate hypervisor FD file to viommu.
VMM:
struct iommu_viommu_hypervisor hypervisor = {
.vm_fd = vm_fd,
};
struct iommu_viommu_alloc alloc = {
.size = sizeof(alloc),
.type = IOMMU_VIOMMU_TYPE_HYPERVISOR,
.dev_id = dev_id,
.data_len = sizeof(hypervisor),
.data_uptr = (uintptr_t)&hypervisor,
};
rc = do_ioctl(iommufd, IOMMU_VIOMMU_ALLOC, &alloc,
"IOMMU_VIOMMU_ALLOC");Since mshv creates partition file with anon_inode_getfile() which does a try_module_get(fops->owner), then in mshv_viommu_init, it does fget(hypervisor.vm_fd) which will indirectly keep the module pinned.
I guess we have to enforce this pattern to make it universally viable?
Thanks,
Jacob
Tian, Kevin
maintainerRE: [PATCH v8 12/15] iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations
Quoted 25 lines
From: Jacob Pan <[email protected]>
Sent: Friday, November 6, 2026 1:16 AMHi Jason,
Quoted 8 lines · On Wed, 4 Nov 2026 20:49:34 -0400 Jason Gunthorpe <[email protected]> wrote:
Quoted 3 lines · Jacob Pan
I am thinking MSHV/pKVM can register an assignment-only
tsm_devto leverage this? Could PCI/TSM support this role without device-security?It is certainly not what TSM was originally imagined for, but with our new arrangement, yah maybe so?
I will rebase on this set and send out patches for a detailed review.
Quoted 7 lines
TSM basically just becomes this weird overlay driver on the PCI device that does stuff. Some of the stuff it might do:
- Register an attestation subsystem device to do SPDM
- Register to allow iommufd to create a viommu
- Some status sysfs ?
- ??
Quoted 8 lines
So we may expand pci tsm to have more than one role? currently it has one.
intpci_tsm_register(struct tsm_dev *tsm_dev){
...roles = is_link_tsm(tsm_dev) + is_devsec_tsm(tsm_dev) + is_assignment_tsm(tsm_dev); if (roles != 1) return -EINVAL;
Not sure what'd be a proper name for this, but it sounds to me that it tries to over-abstract something into tsm. The reused logic related to assignment or viommu is relatively small, so the other option below sounds better if it can work.
Or it may be the original provider framework that Aneesh proposed, so tsm/MSHV/pKVM can each register its own backend...
Re: [PATCH v8 12/15] iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations
Quoted 34 lines · Kevin
Quoted 25 lines
From: Jacob Pan <[email protected]>
Sent: Friday, November 6, 2026 1:16 AMHi Jason,
Quoted 8 lines · On Wed, 4 Nov 2026 20:49:34 -0400 Jason Gunthorpe <[email protected]> wrote:
Quoted 3 lines · Jacob Pan
I am thinking MSHV/pKVM can register an assignment-only
tsm_devto leverage this? Could PCI/TSM support this role without device-security?It is certainly not what TSM was originally imagined for, but with our new arrangement, yah maybe so?
I will rebase on this set and send out patches for a detailed review.
Quoted 7 lines
TSM basically just becomes this weird overlay driver on the PCI device that does stuff. Some of the stuff it might do:
- Register an attestation subsystem device to do SPDM
- Register to allow iommufd to create a viommu
- Some status sysfs ?
- ??Quoted 8 lines
So we may expand pci tsm to have more than one role? currently it has one.
intpci_tsm_register(struct tsm_dev *tsm_dev){
...roles = is_link_tsm(tsm_dev) + is_devsec_tsm(tsm_dev) + is_assignment_tsm(tsm_dev); if (roles != 1) return -EINVAL;
Quoted 7 lines
Not sure what'd be a proper name for this, but it sounds to me that it tries to over-abstract something into tsm. The reused logic related to assignment or viommu is relatively small, so the other option below sounds better if it can work.
Or it may be the original provider framework that Aneesh proposed, so tsm/MSHV/pKVM can each register its own backend...
That is definately undesirable for TSM
I think the very best answer for this is to get the viommu from the "kvm" fd, I just don't quite know how you implement it..
Maybe just register something like a notifier chain where the struct file * and the viommu type value goes in and a viommu object comes back out?
Jason
Tian, Kevin
maintainerRE: [PATCH v8 12/15] iommufd/viommu: Allow PCI TSM backends to provide vIOMMU operations
In reply to Aneesh Kumar K.V (Arm)Quoted 5 lines
From: Aneesh Kumar K.V (Arm) <[email protected]>
Sent: Thursday, November 5, 2026 12:16 AMIn confidential-computing configurations, a vIOMMU may be implemented by a TEE Security Manager rather than by the physical IOMMU driver. Such an
TSM is the firmware. Here it is the TSM driver.
Quoted 10 lines
implementation also needs the TSM identity when creating and validating vdevices.
Allow
VIOMMU_ALLOCto first query the TSM associated with the target PCI device for vIOMMU operations. Fall back to the physical IOMMU driver when the TSM does not provide operations for the requested vIOMMU type.Resolve the PCI TSM association under
pci_tsm_rwsem, pin the module providing the operations, and take a TSM device reference. Store the TSM device in the vIOMMU and release both references on allocation failure,
what does "both references" mean?
Quoted 11 lines
fallback to the physical IOMMU driver, or vIOMMU destruction. Keep the module pinned until after dropping the device reference, since the final put may release the TSM device.
The device reference preserves the TSM allocation. Backends providing vIOMMU operations must not unregister their TSM while the module is pinned, since the reference does not prevent backend resource teardown.
This enables the Realm vIOMMU implementation to provide its operations through the PCI TSM backend and retain the TSM identity needed by its vdevices.
It's not specific to Realm vIOMMU, and Realm is never explained in prior patches.
drivers/iommu/iommufd/viommu.c int iommufd_viommu_alloc_ioctl(struct · line 61
Quoted 13 lines
drivers/iommu/iommufd/viommu.c +4
-2@@ -49,15 +52,33 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) rc = -EOPNOTSUPP ; goto out_put_idev; } -if (!iommu_dev->ops->get_viommu_ops) { - rc = -EOPNOTSUPP ; + tsm_dev =tsm_get_device (idev->dev); +if (IS_ERR (tsm_dev)) { + rc =PTR_ERR (tsm_dev); + tsm_dev =NULL ; goto out_put_idev; }
Is it legal to have a device connected in tsm (i.e. with a valid pdev->tsm) but one wants to assign it to a non-confidential guest using a non-cc vIOMMU type?
If yes we want to fallback to the physical IOMMU driver even when tsm_get_device() returns an error.
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 13/15 iommufd/viommu: Allow vIOMMUs without a parent HWPT
In reply to Aneesh Kumar K.V (Arm)VIOMMU_ALLOC currently requires a nesting parent HWPT_PAGING even when the selected vIOMMU implementation has no use for one. This would force implementations such as the following Arm Realm vIOMMU to create an unused parent.
Add IOMMUFD_VIOMMU_NO_HWPT so an implementation can opt out. Require hwpt_id to be zero in that case and pass a NULL parent domain to its initialization callback. For implementations that require a HWPT, verify that the parent supports nesting and its domain belongs to the device's physical IOMMU driver before invoking viommu_init().
Reject nested domain and hardware queue allocation for a vIOMMU without a parent, and document the two allocation modes in the UAPI.
--- Documentation/userspace-api/iommufd.rst | 44 +++++++++--------- drivers/iommu/iommufd/hw_pagetable.c | 14 ++++-- drivers/iommu/iommufd/viommu.c | 62 +++++++++++++++---------- include/linux/iommufd.h | 13 ++++-- include/uapi/linux/iommufd.h | 3 +- 5 files changed, 81 insertions(+), 55 deletions(-)
Documentation/userspace-api/iommufd.rst +23 -21
@@ -82,11 +82,10 @@ Following IOMMUFD objects are exposed to userspace: * Direct assigned invalidation queues * Direct assigned interrupts - Such a vIOMMU object generally has the access to a nesting parent pagetable - to support some HW-accelerated virtualization features. So, a vIOMMU object - must be created given a nesting parent HWPT_PAGING object, and then it would - encapsulate that HWPT_PAGING object. Therefore, a vIOMMU object can be used - to allocate an HWPT_NESTED object in place of the encapsulated HWPT_PAGING. + A vIOMMU object may encapsulate a nesting parent HWPT_PAGING object to + support HW-accelerated virtualization features. Whether a parent is required + is determined by the selected vIOMMU implementation. Only a parent-backed + vIOMMU can be used to allocate an HWPT_NESTED object. .. note::
@@ -226,9 +225,9 @@ creating the objects and links:: flag is set. 4. IOMMUFD_OBJ_HWPT_NESTED can be only manually created via the IOMMU_HWPT_ALLOC - uAPI, provided an hwpt_id or a viommu_id of a vIOMMU object encapsulating a - nesting parent HWPT_PAGING via @pt_id to associate the new HWPT_NESTED object - to the corresponding HWPT_PAGING object. The associating HWPT_PAGING object + uAPI, provided an hwpt_id or a viommu_id of a parent-backed vIOMMU object + via @pt_id to associate the new HWPT_NESTED object to the corresponding + HWPT_PAGING object. The associating HWPT_PAGING object must be a nesting parent manually allocated via the same uAPI previously with an IOMMU_HWPT_ALLOC_NEST_PARENT flag, otherwise the allocation will fail. The allocation will be further validated by the IOMMU driver to ensure that the
@@ -245,25 +244,28 @@ creating the objects and links:: of the object passed in via the @pt_id field of struct iommu_hwpt_alloc. 5. IOMMUFD_OBJ_VIOMMU can be only manually created via the IOMMU_VIOMMU_ALLOC - uAPI, provided a dev_id (for the device's physical IOMMU to back the vIOMMU) - and an hwpt_id (to associate the vIOMMU to a nesting parent HWPT_PAGING). The - iommufd core will link the vIOMMU object to the struct iommu_device that the - struct device is behind. And an IOMMU driver can implement a viommu_alloc op - to allocate its own vIOMMU data structure embedding the core-level structure - iommufd_viommu and some driver-specific data. If necessary, the driver can - also configure its HW virtualization feature for that vIOMMU (and thus for - the VM). Successful completion of this operation sets up the linkages between - the vIOMMU object and the HWPT_PAGING, then this vIOMMU object can be used - as a nesting parent object to allocate an HWPT_NESTED object described above. + uAPI, provided a dev_id identifying the device used to select the vIOMMU + implementation and, if the selected implementation requires a nesting + parent HWPT_PAGING, an hwpt_id. The hwpt_id must be zero for an + implementation that does not use a parent. A physical-IOMMU implementation + links the vIOMMU object to the struct iommu_device behind the device. Other + implementations, such as a TSM associated with the device, can provide their + own backing and device association. An implementation can allocate its own + vIOMMU data structure embedding the core-level structure iommufd_viommu and + some implementation-specific data. If necessary, it can also configure its + virtualization resources for that vIOMMU (and thus for the VM). A + parent-backed vIOMMU can be used as a nesting parent object to allocate an + HWPT_NESTED object described above. 6. IOMMUFD_OBJ_VDEVICE can be only manually created via the IOMMU_VDEVICE_ALLOC uAPI, provided a viommu_id for an iommufd_viommu object and a dev_id for an iommufd_device object. The vDEVICE object will be the binding between these two parent objects. Another @virt_id will be also set via the uAPI providing the iommufd core an index to store the vDEVICE object to a vDEVICE array per - vIOMMU. If necessary, the IOMMU driver may choose to implement a vdevce_alloc - op to init its HW for virtualization feature related to a vDEVICE. Successful - completion of this operation sets up the linkages between vIOMMU and device. + vIOMMU. A physical-IOMMU implementation requires the device to be behind the + same iommu_device as the vIOMMU. Other implementations validate the device + association while initializing the vDEVICE. Successful initialization sets + up the linkage between the vIOMMU and device. A device can only bind to an iommufd due to DMA ownership claim and attach to at most one IOAS object (no support of PASID yet).
drivers/iommu/iommufd/hw_pagetable.c +10 -4
@@ -306,6 +306,10 @@ iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,return ERR_PTR (-EOPNOTSUPP );if (!user_data->len)return ERR_PTR (-EOPNOTSUPP ); +if (!viommu->hwpt) +return ERR_PTR (-EOPNOTSUPP ); +if (!viommu->iommu_dev) +return ERR_PTR (-EOPNOTSUPP );if (!viommu->ops || !viommu->ops->alloc_domain_nested)return ERR_PTR (-EOPNOTSUPP );
@@ -404,10 +408,12 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)struct iommufd_viommu *viommu; viommu = container_of(pt_obj, struct iommufd_viommu, obj); - iommu_dev =iommufd_device_get_iommu_dev (idev); -if (!iommu_dev || viommu->iommu_dev != iommu_dev) { - rc = -EINVAL ; - goto out_unlock; +if (viommu->iommu_dev) { + iommu_dev =iommufd_device_get_iommu_dev (idev); +if (!iommu_dev || viommu->iommu_dev != iommu_dev) { + rc = -EINVAL ; + goto out_unlock; + } } hwpt_nested =iommufd_viommu_alloc_hwpt_nested ( viommu, cmd->flags, &user_data);
drivers/iommu/iommufd/viommu.c +37 -25
125 lines not on this page · raw message
include/linux/iommufd.h +9 -4
@@ -6,6 +6,7 @@#ifndef __LINUX_IOMMUFD_H#define __LINUX_IOMMUFD_H +#include <linux/bits.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/iommu.h>
@@ -102,6 +103,7 @@ void iommufd_ctx_get(struct iommufd_ctx *ictx);struct iommufd_viommu {struct iommufd_object obj;struct iommufd_ctx *ictx; +/* Physical IOMMU backing, or NULL. */ struct iommu_device *iommu_dev;struct iommufd_hwpt_paging *hwpt;struct file *kvm_file;
@@ -148,17 +150,19 @@ struct iommufd_hw_queue {void (*destroy)(struct iommufd_hw_queue *hw_queue); }; +#define IOMMUFD_VIOMMU_NO_HWPT BIT(0) +/** * struct iommufd_viommu_ops - vIOMMU specific operations +* @flags: Properties needed by the core before vIOMMU initialization * @get_viommu_size: Get the size of a driver-level vIOMMU structure for a given * @dev corresponding to @viommu_type. Driver should return 0 * if vIOMMU isn't supported accordingly. It is required for * driver to use the VIOMMU_STRUCT_SIZE macro to sanitize the * driver-level vIOMMU structure related to the core one -* @viommu_init: Init the driver-level struct of an iommufd_viommu on a physical -* IOMMU instance @viommu->iommu_dev, as the set of virtualization -* resources shared/passed to user space IOMMU instance. Associate -* it with a nesting @parent_domain. +* @viommu_init: Initialize the selected driver-level iommufd_viommu for @dev. +* @parent_domain is the nesting parent, or NULL for an +* implementation that does not use a parent HWPT. * @destroy: Clean up all driver-specific parts of an iommufd_viommu. The memory * of the vIOMMU will be free-ed by iommufd core after calling this op * @alloc_domain_nested: Allocate a IOMMU_DOMAIN_NESTED on a vIOMMU that holds a
@@ -201,6 +205,7 @@ struct iommufd_hw_queue {* does, it should set it to the @hw_queue->destroy pointer */ struct iommufd_viommu_ops { +unsigned long flags;size_t (*get_viommu_size)(struct device *dev,enum iommu_viommu_type type); int (*viommu_init)(struct iommufd_viommu *viommu, struct device *dev,
include/uapi/linux/iommufd.h +2 -1
@@ -1138,7 +1138,8 @@ struct iommu_viommu_tegra241_cmdqv {* @flags: Must be 0 * @type: Type of the virtual IOMMU. Must be defined in enum iommu_viommu_type * @dev_id: The device's physical IOMMU will be used to back the virtual IOMMU -* @hwpt_id: ID of a nesting parent HWPT to associate to +* @hwpt_id: ID of a nesting parent HWPT to associate to. Must be zero when the +* selected vIOMMU implementation does not use a parent HWPT * @out_viommu_id: Output virtual IOMMU ID for the allocated object * @data_len: Length of the type specific data * @__reserved: Must be 0
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 13/15] iommufd/viommu: Allow vIOMMUs without a parent HWPT
Sashiko has reviewed this patch and found no issues.
Quoted 14 lines
VIOMMU_ALLOCcurrently requires a nesting parentHWPT_PAGINGeven when the selected vIOMMU implementation has no use for one. This would force implementations such as the following Arm Realm vIOMMU to create an unused parent.Add
IOMMUFD_VIOMMU_NO_HWPTso an implementation can opt out. Requirehwpt_idto be zero in that case and pass a NULL parent domain to its initialization callback. For implementations that require a HWPT, verify that the parent supports nesting and its domain belongs to the device's physical IOMMU driver before invokingviommu_init().Reject nested domain and hardware queue allocation for a vIOMMU without a parent, and document the two allocation modes in the UAPI.
Quoted 9 lines
Cc Jason Gunthorpe <[email protected]>Cc Kevin Tian <[email protected]>Cc Jonathan Corbet <[email protected]>Cc Shuah Khan <[email protected]>Cc Randy Dunlap <[email protected]>Cc Joerg Roedel <[email protected]>Cc Will Deacon <[email protected]>Cc Robin Murphy <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=13
Re: [PATCH v8 13/15] iommufd/viommu: Allow vIOMMUs without a parent HWPT
In reply to Aneesh Kumar K.V (Arm)Quoted 4 lines · Aneesh Kumar K.V (Arm)
VIOMMU_ALLOCcurrently requires a nesting parentHWPT_PAGINGeven when the selected vIOMMU implementation has no use for one. This would force implementations such as the following Arm Realm vIOMMU to create an unused parent.
IIRC, MSI on ARM still goes through the NS pathway. So, it wouldn't be "unused" if the device attaches to it (or its nested hwpts)?
By dropping the nesting parent from VIOMMU, it just simplifies the attachment to an S1 paging hwpt, right?
Nicolin
Re: [PATCH v8 13/15] iommufd/viommu: Allow vIOMMUs without a parent HWPT
Quoted 8 lines · Nicolin Chen
Quoted 4 lines · Aneesh Kumar K.V (Arm)
VIOMMU_ALLOCcurrently requires a nesting parentHWPT_PAGINGeven when the selected vIOMMU implementation has no use for one. This would force implementations such as the following Arm Realm vIOMMU to create an unused parent.IIRC, MSI on ARM still goes through the NS pathway. So, it wouldn't be "unused" if the device attaches to it (or its nested hwpts)?
It is unused by the RMM VIOMMU. I think that is what is Aneesh is explaining.
Quoted 2 lines
By dropping the nesting parent from VIOMMU, it just simplifies the attachment to an S1 paging hwpt, right?
Yes.
The VMM must always maintain a S1 paging attachment that is the identity map of the VM on all devices, same action as when there is no viommu.
Jason
Aneesh Kumar K.V
maintainerRe: [PATCH v8 13/15] iommufd/viommu: Allow vIOMMUs without a parent HWPT
Quoted 7 lines · Jason Gunthorpe
Quoted 6 lines · Nicolin Chen
Quoted 4 lines · Aneesh Kumar K.V (Arm)
VIOMMU_ALLOCcurrently requires a nesting parentHWPT_PAGINGeven when the selected vIOMMU implementation has no use for one. This would force implementations such as the following Arm Realm vIOMMU to create an unused parent.
Quoted 6 lines
Quoted 2 lines
IIRC, MSI on ARM still goes through the NS pathway. So, it wouldn't be "unused" if the device attaches to it (or its nested hwpts)?
It is unused by the RMM VIOMMU. I think that is what is Aneesh is explaining.
We create an IOMMU_HWPT_ALLOC_NEST_PARENT HWPT outside the vIOMMU and attach it before allocating the vIOMMU. This HWPT is used for untrusted devices and MSI-X.
The sequence is:
ret = __iommufd_configure_device(kvm, vdev);
ret = iommufd_get_hwpt(vdev, hwpt_flags, &hwpt_id);
ret = iommufd_attach_hwpt(vdev, hwpt_id);
ret = iommufd_create_vdevice(kvm, vdev);
viommu = iommufd_alloc_viommu(kvm, vdev, vdev->link_tsm_name);
if (ioctl(iommu_fd, IOMMU_VDEVICE_ALLOC, &alloc_vdev)) {Alloc viommu does the below:
viommu->_viommu.size = sizeof(struct iommu_viommu_alloc);
viommu->_viommu.flags = 0;
viommu->_viommu.type = kvm__arch_get_viommu_type(kvm);
viommu->_viommu.dev_id = vdev->bound_devid;
viommu->_viommu.hwpt_id = 0;
if (ioctl(iommu_fd, IOMMU_VIOMMU_ALLOC, &viommu->_viommu)) {
vfio_dev_err(vdev, "failed to allocate VIOMMU %d", -errno);
free(viommu);
return NULL;
}Quoted 10 lines
Quoted 3 lines
By dropping the nesting parent from VIOMMU, it just simplifies the attachment to an S1 paging hwpt, right?
Yes.
The VMM must always maintain a S1 paging attachment that is the identity map of the VM on all devices, same action as when there is no viommu.
I am currently creating an S2 paging attachment. This results in a configuration with S1 disabled and S2 enabled, which is used by all untrusted devices.
-aneesh
Re: [PATCH v8 13/15] iommufd/viommu: Allow vIOMMUs without a parent HWPT
Quoted 7 lines · Aneesh Kumar K.V
Quoted 6 lines · Jason Gunthorpe
Quoted 2 lines · Nicolin Chen
By dropping the nesting parent from VIOMMU, it just simplifies the attachment to an S1 paging hwpt, right?
Yes.
Quoted 8 lines
Quoted 4 lines
The VMM must always maintain a S1 paging attachment that is the identity map of the VM on all devices, same action as when there is no viommu.
I am currently creating an S2 paging attachment. This results in a configuration with S1 disabled and S2 enabled, which is used by all untrusted devices.
For validation, that would work. Yet "no viommu" should would be S1 paging, i.e. hwpt allocated without IOMMU_HWPT_ALLOC_NEST_PARENT.
For reference, QEMU has this:
https://github.com/qemu/qemu/blob/master/hw/vfio/iommufd.c#L467
Thanks
Nicolin
Re: [PATCH v8 13/15] iommufd/viommu: Allow vIOMMUs without a parent HWPT
Quoted 13 lines · Nicolin Chen
Quoted 12 lines · Aneesh Kumar K.V
Quoted 10 lines · Jason Gunthorpe
Quoted 2 lines · Nicolin Chen
By dropping the nesting parent from VIOMMU, it just simplifies the attachment to an S1 paging hwpt, right?
Yes.
The VMM must always maintain a S1 paging attachment that is the identity map of the VM on all devices, same action as when there is no viommu.
Quoted 6 lines
Quoted 3 lines
I am currently creating an S2 paging attachment. This results in a configuration with S1 disabled and S2 enabled, which is used by all untrusted devices.
For validation, that would work. Yet "no viommu" should would be S1 paging, i.e. hwpt allocated without
IOMMU_HWPT_ALLOC_NEST_PARENT.
It doesn't matter, consistently using a single S2 for everything is probably better..
The VMM should try to minimize the number of hwpts involved, ideally just one per NUMA
Jason
Re: [PATCH v8 13/15] iommufd/viommu: Allow vIOMMUs without a parent HWPT
Quoted 18 lines · Jason Gunthorpe
Quoted 17 lines · Nicolin Chen
Quoted 15 lines · Aneesh Kumar K.V
Quoted 10 lines · Jason Gunthorpe
Quoted 2 lines · Nicolin Chen
By dropping the nesting parent from VIOMMU, it just simplifies the attachment to an S1 paging hwpt, right?
Yes.
The VMM must always maintain a S1 paging attachment that is the identity map of the VM on all devices, same action as when there is no viommu.
I am currently creating an S2 paging attachment. This results in a configuration with S1 disabled and S2 enabled, which is used by all untrusted devices.
Quoted 5 lines
Quoted 2 lines
For validation, that would work. Yet "no viommu" should would be S1 paging, i.e. hwpt allocated without
IOMMU_HWPT_ALLOC_NEST_PARENT.It doesn't matter, consistently using a single S2 for everything is probably better..
Hmm, I am confused: if we use a nesting parent, why do we need to allow a viommu without a parent HWPT here?
Nicolin
Re: [PATCH v8 13/15] iommufd/viommu: Allow vIOMMUs without a parent HWPT
Quoted 19 lines · Nicolin Chen
Quoted 18 lines · Jason Gunthorpe
Quoted 17 lines · Nicolin Chen
Quoted 15 lines · Aneesh Kumar K.V
Quoted 10 lines · Jason Gunthorpe
Quoted 2 lines · Nicolin Chen
By dropping the nesting parent from VIOMMU, it just simplifies the attachment to an S1 paging hwpt, right?
Yes.
The VMM must always maintain a S1 paging attachment that is the identity map of the VM on all devices, same action as when there is no viommu.
I am currently creating an S2 paging attachment. This results in a configuration with S1 disabled and S2 enabled, which is used by all untrusted devices.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
For validation, that would work. Yet "no viommu" should would be S1 paging, i.e. hwpt allocated without
IOMMU_HWPT_ALLOC_NEST_PARENT.It doesn't matter, consistently using a single S2 for everything is probably better..
Hmm, I am confused: if we use a nesting parent, why do we need to allow a viommu without a parent HWPT here?
It is because it doesn't *use* it. It is a confusing to pass in a hwpt that is never used for anything.
Jason
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 14/15 PCI/TSM: Wait for device contexts before removing a DSM
In reply to Aneesh Kumar K.V (Arm)A PCI Device Security Manager (DSM) on physical function 0 (PF0) can be removed while a sibling function still has an IOMMUFD vDEVICE object. A vDEVICE represents a physical device within a virtual IOMMU for a guest and holds a PCI/TSM context for its lifetime.
The context holds PCI device references to both the assigned function and its DSM, but these references do not keep the PF0 Data Object Exchange (DOE) mailbox alive. Ignoring -EBUSY from link disconnect allows removal to reach pci_doe_destroy() while the context can still use the mailbox.
Reject new contexts and subfunction attachments once DSM removal starts. Wait for existing contexts to be released before disconnecting the link and allowing PCI removal to destroy the mailbox.
As with VFIO PCI removal, removal waits for userspace to release the device. This patch does not provide an eventfd notification to request that release.
--- drivers/pci/tsm/core.c | 75 ++++++++++++++++++++++++++++++++++++++--- include/linux/pci-tsm.h | 9 +++-- 2 files changed, 77 insertions(+), 7 deletions(-)
drivers/pci/tsm/core.c +70 -5
@@ -10,10 +10,13 @@#include <linux/bitfield.h> #include <linux/iommufd.h> +#include <linux/jiffies.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/pci-doe.h> #include <linux/pci-tsm.h> +#include <linux/pid.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/tsm.h>
@@ -355,7 +358,15 @@ struct pci_tsm_context *pci_tsm_context_get(struct pci_dev *pdev,return ERR_PTR (-ENOMEM );guard (mutex)(&pf0->lock); - pf0->context_users++; +if (pf0->removing) { +kfree (context); +return ERR_PTR (-ENODEV ); + } + +if (!pf0->dsm_context_users) +reinit_completion (&pf0->dsm_context_drain_complete); + + pf0->dsm_context_users++; context->pf0 = pf0; context->pdev =pci_dev_get (pdev); context->dsm_dev =pci_dev_get (pf0->base_tsm.pdev);
@@ -369,8 +380,12 @@ void pci_tsm_context_put(struct pci_tsm_context *context)down_read (&pci_tsm_rwsem);mutex_lock (&pf0->lock); -if (!WARN_ON (!pf0->context_users)) - pf0->context_users--; +if (WARN_ON (!pf0->dsm_context_users)) + goto out_unlock; + +if (!--pf0->dsm_context_users) +complete_all (&pf0->dsm_context_drain_complete); +out_unlock:mutex_unlock (&pf0->lock);up_read (&pci_tsm_rwsem);
@@ -418,7 +433,7 @@ static int __pci_tsm_disconnect(struct pci_dev *pdev)* teardown. */ guard (mutex)(&tsm_pf0->lock); -if (tsm_pf0->context_users) +if (tsm_pf0->dsm_context_users)return -EBUSY ;pci_tsm_walk_fns_reverse (pdev, remove_fn,NULL ); ops->disconnect (pdev);
@@ -454,6 +469,10 @@ static ssize_t disconnect_store(struct device *dev,if (!sysfs_streq (buf,dev_name (&tsm_dev->dev)))return -EINVAL ; +if (is_link_tsm (tsm_dev) &&is_pci_tsm_pf0 (pdev) && +to_pci_tsm_pf0 (pdev->tsm)->removing) +return -ENODEV ; + rc =pci_tsm_disconnect (pdev);if (rc)return rc;
@@ -664,6 +683,9 @@ int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm, struct tsm_dev *tsm_dev) {mutex_init (&tsm->lock); +init_completion (&tsm->dsm_context_drain_complete); + tsm->dsm_context_users =0 ; + tsm->removing = false; tsm->doe_mb =pci_find_doe_mailbox (pdev,PCI_VENDOR_ID_PCI_SIG ,PCI_DOE_FEATURE_CMA );if (!tsm->doe_mb) {
@@ -752,8 +774,47 @@ static void __pci_tsm_destroy(struct pci_dev *pdev, struct tsm_dev *tsm_dev)void pci_tsm_destroy (struct pci_dev *pdev) { -guard (rwsem_write)(&pci_tsm_rwsem); +struct pci_tsm_pf0 *pf0 =NULL ; +struct completion *drained; +bool interrupted = false; +long rc; + +down_write (&pci_tsm_rwsem); +if (pdev->tsm && +is_link_tsm (pdev->tsm->tsm_dev) &&is_pci_tsm_pf0 (pdev)) { + pf0 =to_pci_tsm_pf0 (pdev->tsm); + drained = &pf0->dsm_context_drain_complete; +mutex_lock (&pf0->lock); + pf0->removing = true; + +mutex_unlock (&pf0->lock); +/* An unused DSM may never have completed dsm_context_drain_complete. */ +if (pf0->dsm_context_users) + rc =try_wait_for_completion (drained); +else + rc =1 ; + +/* Context release needs the read side of pci_tsm_rwsem. */ +up_write (&pci_tsm_rwsem); +while (rc <=0 ) { +if (interrupted) { + rc =wait_for_completion_timeout (drained,HZ *10 ); + }else { + rc =wait_for_completion_interruptible_timeout (drained, +HZ *10 ); +if (rc <0 ) { + interrupted = true; +pci_warn (pdev,"Task \"%s\" (%d) blocked until vdevices are released\n" , + current->comm,task_pid_nr (current)); + } + } +if (!rc) +pci_warn (pdev,"TSM connection is in use, waiting for vdevices\n" ); + } +down_write (&pci_tsm_rwsem); + }__pci_tsm_destroy (pdev,NULL ); +up_write (&pci_tsm_rwsem); }void pci_tsm_init (struct pci_dev *pdev)
@@ -781,6 +842,10 @@ void pci_tsm_init(struct pci_dev *pdev)if (!dsm->tsm)return ; +if (is_link_tsm (dsm->tsm->tsm_dev) && +to_pci_tsm_pf0 (dsm->tsm)->removing) +return ; +probe_fn (pdev, dsm); } }
include/linux/pci-tsm.h +7 -2
@@ -1,6 +1,7 @@/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __PCI_TSM_H#define __PCI_TSM_H +#include <linux/completion.h> #include <linux/mutex.h> #include <linux/pci.h> #include <uapi/linux/iommufd.h>
@@ -107,14 +108,18 @@ struct pci_tsm {* struct pci_tsm_pf0 - Physical Function 0 TDISP link context * @base_tsm: generic core "tsm" context * @lock: mutual exclustion for pci_tsm_ops invocation -* @context_users: live per-function contexts on this PF0; a nonzero count +* @dsm_context_users: live per-function contexts on this PF0; a nonzero count * blocks link disconnect +* @dsm_context_drain_complete: completed when the last context is released +* @removing: reject new contexts while the DSM is being removed * @doe_mb: PCIe Data Object Exchange mailbox */ struct pci_tsm_pf0 {struct pci_tsm base_tsm;struct mutex lock; -unsigned int context_users; +unsigned int dsm_context_users; +struct completion dsm_context_drain_complete; +bool removing;struct pci_doe_mb *doe_mb; };
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 14/15] PCI/TSM: Wait for device contexts before removing a DSM
Sashiko has reviewed this patch and found no issues.
Quoted 18 lines
A PCI Device Security Manager (DSM) on physical function 0 (PF0) can be removed while a sibling function still has an IOMMUFD vDEVICE object. A vDEVICE represents a physical device within a virtual IOMMU for a guest and holds a PCI/TSM context for its lifetime.
The context holds PCI device references to both the assigned function and its DSM, but these references do not keep the PF0 Data Object Exchange (DOE) mailbox alive. Ignoring
-EBUSYfrom link disconnect allows removal to reachpci_doe_destroy()while the context can still use the mailbox.Reject new contexts and subfunction attachments once DSM removal starts. Wait for existing contexts to be released before disconnecting the link and allowing PCI removal to destroy the mailbox.
As with VFIO PCI removal, removal waits for userspace to release the device. This patch does not provide an eventfd notification to request that release.
Quoted 10 lines
Cc Bjorn Helgaas <[email protected]>Cc Jonathan Cameron <[email protected]>Cc Alexey Kardashevskiy <[email protected]>Cc Xu Yilun <[email protected]>Cc Lukas Wunner <[email protected]>Cc Samuel Ortiz <[email protected]>Cc Suzuki K Poulose <[email protected]>Cc Jason Gunthorpe <[email protected]>Cc Kevin Tian <[email protected]>Signed-off-by Aneesh Kumar K.V (Arm) <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=14
Aneesh Kumar K.V (Arm)
maintainerPATCH v8 15/15 PCI/TSM: Wait for users during unregister
In reply to Aneesh Kumar K.V (Arm)Wait for TEE Security Manager (TSM) users before tearing down PCI connections, including IOMMUFD virtual IOMMUs (vIOMMUs) with no vDEVICE objects. A vDEVICE represents a physical device within a guest's virtual IOMMU.
Add registration_lock to protect operations against unregister. New operations try the read lock. Long-lived users increment the user refcount under that lock. Unregister holds the write lock while dropping the registration reference, waiting for users and tearing down PCI connections.
PCI Device Security Manager (DSM) removal holds the registration read lock while waiting for its contexts and completing teardown. If unregister prevents taking the read lock, retain a device reference and wait for all TSM users instead.
--- drivers/crypto/ccp/sev-dev-tsm.c | 4 +- drivers/pci/tsm/core.c | 151 ++++++++++++++++++++++--------- drivers/virt/coco/tsm-core.c | 137 ++++++++++++++++++++++++++-- include/linux/pci-tsm.h | 7 +- include/linux/tsm.h | 15 ++- 5 files changed, 258 insertions(+), 56 deletions(-)
drivers/crypto/ccp/sev-dev-tsm.c +2 -2
@@ -392,8 +392,8 @@ void sev_tsm_init_locked(struct sev_device *sev, void *tio_status_page)void sev_tsm_uninit (struct sev_device *sev) { -if (sev->tsmdev) -tsm_unregister (sev->tsmdev); +if (sev->tsmdev &&WARN_ON (tsm_unregister (sev->tsmdev))) +return ; sev->tsmdev =NULL ; }
drivers/pci/tsm/core.c +108 -43
234 lines not on this page · raw message
drivers/virt/coco/tsm-core.c +131 -6
178 lines not on this page · raw message
include/linux/pci-tsm.h +3 -4
@@ -24,10 +24,9 @@ struct tsm_dev;* or NULL if unsupported * @owner: Module providing the vIOMMU operations, if built as a module * -* The vIOMMU owner holds a TSM device reference and pins the backend module -* before querying @viommu_get_ops, releasing both after vIOMMU destruction. -* A backend providing this callback must not unregister its TSM while the -* module is pinned. +* The vIOMMU owner holds a TSM user reference, a device reference and a +* backend module reference before querying @viommu_get_ops, releasing them +* after vIOMMU destruction. TSM unregister waits for the user reference. * * This operations are mutually exclusive either a tsm_dev instance * manages physical link properties or it manages function security
include/linux/tsm.h +14 -1
@@ -2,6 +2,9 @@#ifndef __TSM_H#define __TSM_H +#include <linux/completion.h> +#include <linux/refcount.h> +#include <linux/rwsem.h> #include <linux/sizes.h> #include <linux/types.h> #include <linux/uuid.h>
@@ -116,6 +119,13 @@ struct tsm_dev {struct device dev;int id;const struct pci_tsm_ops *pci_ops; +/* Read side protects operations; write side protects unregister. */ +struct rw_semaphore registration_lock; +/* Protected by registration_lock; prevents reuse after unregister. */ +bool unregistering; +/* Incremented under registration_lock; unregister waits for zero. */ +refcount_t users; +struct completion users_drain_complete; }; DEFINE_FREE(put_tsm_dev, struct tsm_dev *,
@@ -124,10 +134,13 @@ DEFINE_FREE(put_tsm_dev, struct tsm_dev *,int tsm_report_register (const struct tsm_report_ops *ops,void *priv);int tsm_report_unregister (const struct tsm_report_ops *ops);struct tsm_dev *tsm_register (struct device *parent,struct pci_tsm_ops *ops); -void tsm_unregister (struct tsm_dev *tsm_dev); +int tsm_unregister (struct tsm_dev *tsm_dev);struct tsm_dev *find_tsm_dev (int id);#ifdef CONFIG_TSM +struct tsm_dev *tsm_get (struct tsm_dev *tsm_dev); +bool tsm_trylock_registration (struct tsm_dev *tsm_dev); +void tsm_unlock_registration (struct tsm_dev *tsm_dev);struct tsm_dev *tsm_get_device (struct device *dev);void tsm_put_device (struct tsm_dev *tsm_dev); const struct iommufd_viommu_ops *tsm_viommu_get_ops(struct tsm_dev *tsm_dev,
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v8 15/15] PCI/TSM: Wait for users during unregister
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] PCI/TSM: use-after-free in tsm_unregister() and sev_tsm_uninit() - [Low] PCI/TSM: mixing guard()…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] PCI/TSM: use-after-free in tsm_unregister() and sev_tsm_uninit()
- [Low] PCI/TSM: mixing guard() and goto in pci_tsm_context_get() and pci_tsm_init() --
commit b2546a16b6b6a681e70bba7a7e4c0fbd2665c584
Author: Aneesh Kumar K.V (Arm) <[email protected]>
PCI/TSM: Wait for users during unregister
This commit adds registration_lock and a user reference count to struct tsm_dev so that tsm_unregister() and pci_tsm_destroy() wait for active TSM users, including IOMMUFD virtual IOMMUs without vDEVICE objects, before tearing down PCI connections.
Quoted 5 lines
drivers/crypto/ccp/sev-dev-tsm.c +0
-0
drivers/crypto/ccp/sev-dev-tsm.c void sev_tsm_init_locked(struct sev_device *sev, void *tio_status_page) · line 399
Quoted 10 lines
drivers/crypto/ccp/sev-dev-tsm.c +2
-2@@ -392,8 +392,8 @@ void sev_tsm_init_locked(struct sev_device *sev, void *tio_status_page)void sev_tsm_uninit (struct sev_device *sev) { -if (sev->tsmdev) -tsm_unregister (sev->tsmdev); +if (sev->tsmdev &&WARN_ON (tsm_unregister (sev->tsmdev))) +return ; sev->tsmdev =NULL ; }
[Severity: High]
Can a concurrent call to sev_tsm_uninit() while tsm_unregister() is waiting for TSM users trigger a use-after-free or premature teardown?
In drivers/crypto/ccp/sev-dev.c, sev_firmware_shutdown() invokes sev_tsm_uninit() outside sev_cmd_mutex:
drivers/crypto/ccp/sev-dev.c:sev_firmware_shutdown() {
if (sev->tio_status)
sev_tsm_uninit(sev);
sev_snp_unregister_verify_mitigation(sev);
mutex_lock(&sev_cmd_mutex);
__sev_firmware_shutdown(sev, false);
kfree(sev->tio_status);
sev->tio_status = NULL;
mutex_unlock(&sev_cmd_mutex);
}Because tsm_unregister() now sleeps in tsm_wait_for_users() and sev->tsmdev is only cleared after tsm_unregister() returns, a concurrent caller of sev_firmware_shutdown() (such as sev_platform_shutdown() racing with sev_dev_destroy() or sev_pci_exit()) can see non-NULL sev->tio_status and sev->tsmdev and call tsm_unregister(sev->tsmdev) a second time.
Additionally, if tsm_unregister() returns -ENODEV here, sev_tsm_uninit() returns early without clearing sev->tsmdev, and sev_firmware_shutdown() proceeds to run __sev_firmware_shutdown() and free sev->tio_status while the first caller is still blocked in tsm_wait_for_users() or disconnecting PCI devices in pci_tsm_unregister().
Quoted 4 lines
drivers/pci/tsm/core.c +0
-0
[ ... ]
Quoted 52 lines
drivers/pci/tsm/core.c +20
-7@@ -343,24 +352,33 @@ struct pci_tsm_context *pci_tsm_context_get(struct pci_dev *pdev,return ERR_PTR (-EOPNOTSUPP );/* * The vdevice can use a different PCI function from VIOMMU_ALLOC. The -* module pin keeps the vIOMMU's TSM registered, but does not freeze this +* caller must retain its tsm_get_device() reference until the context is +* released. This keeps the TSM registered, but does not freeze this * function's link. Match its current TSM before pinning the link below. */ if (pdev->tsm->tsm_dev != viommu_tsm_dev)return ERR_PTR (-EXDEV ); +if (!tsm_trylock_registration (viommu_tsm_dev)) +return ERR_PTR (-ENODEV ); + pf0 =to_pci_tsm_pf0 (pdev->tsm); -if (!pf0) -return ERR_PTR (-ENXIO ); +if (!pf0) { + context =ERR_PTR (-ENXIO ); + goto out_unlock_registration; + } context =kzalloc_obj (*context); -if (!context) -return ERR_PTR (-ENOMEM ); +if (!context) { + context =ERR_PTR (-ENOMEM ); + goto out_unlock_registration; + } -guard (mutex)(&pf0->lock); +mutex_lock (&pf0->lock);if (pf0->removing) {kfree (context); -return ERR_PTR (-ENODEV ); + context =ERR_PTR (-ENODEV ); + goto out_unlock_mutex; }if (!pf0->dsm_context_users)@@ -370,6 +388,10 @@ struct pci_tsm_context *pci_tsm_context_get(struct pci_dev *pdev, context->pf0 = pf0; context->pdev =pci_dev_get (pdev); context->dsm_dev =pci_dev_get (pf0->base_tsm.pdev); +out_unlock_mutex: +mutex_unlock (&pf0->lock); +out_unlock_registration: +tsm_unlock_registration (viommu_tsm_dev);return context; }EXPORT_SYMBOL_GPL (pci_tsm_context_get);
[ ... ]
Quoted 8 lines
drivers/pci/tsm/core.c +4
-0@@ -842,11 +901,17 @@ void pci_tsm_init(struct pci_dev *pdev)if (!dsm->tsm)return ; + tsm_dev = dsm->tsm->tsm_dev; +if (!tsm_trylock_registration (tsm_dev)) +return ; +
drivers/pci/tsm/core.c void pci_tsm_init(struct pci_dev *pdev) · line 916
Quoted 10 lines
drivers/pci/tsm/core.c +3
-1if (is_link_tsm (dsm->tsm->tsm_dev) &&to_pci_tsm_pf0 (dsm->tsm)->removing) -return ; + goto out_unlock_registration;probe_fn (pdev, dsm); +out_unlock_registration: +tsm_unlock_registration (tsm_dev); } }
[Severity: Low]
This isn't a bug, but pci_tsm_context_get() and pci_tsm_init() both use guard(rwsem_read)(&pci_tsm_rwsem) and now mix scope-based cleanup with goto-based error handling (and pci_tsm_context_get() converts guard(mutex)(&pf0->lock) back to manual mutex_lock() / mutex_unlock() with goto labels).
Could these functions avoid mixing guard() with goto cleanup labels?
Quoted 4 lines
drivers/virt/coco/tsm-core.c +0
-0
[ ... ]
Quoted 25 lines
drivers/virt/coco/tsm-core.c +21
-2+/** +* tsm_unregister() - unregister a TSM after draining its users +* @tsm_dev: TSM to unregister +* +* Blocks new users and waits for references acquired by tsm_get_device(), +* including vIOMMUs without vdevices, and pending DSM removals before tearing +* down PCI connections. The wait cannot be aborted by a signal; userspace +* must release the vIOMMUs and vdevices to let it finish. +* +* Return: 0 on success, or -ENODEV if unregistration has already started. +*/ +int tsm_unregister (struct tsm_dev *tsm_dev) { -/* Remove the class lookup first. */ +down_write (&tsm_dev->registration_lock); +if (tsm_dev->unregistering) { +up_write (&tsm_dev->registration_lock); +return -ENODEV ; + } + tsm_dev->unregistering = true; +device_del (&tsm_dev->dev); -if (tsm_dev->pci_ops) +/* No new user references can be acquired while the write lock is held. */ +tsm_put_user (tsm_dev);
drivers/virt/coco/tsm-core.c struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pci_ops) · line 279
Quoted 10 lines
drivers/virt/coco/tsm-core.c +6
-0+tsm_wait_for_users (tsm_dev); + +if (IS_ENABLED (CONFIG_PCI_TSM ) && tsm_dev->pci_ops)pci_tsm_unregister (tsm_dev); + +up_write (&tsm_dev->registration_lock);put_device (&tsm_dev->dev); +return 0 ; }EXPORT_SYMBOL_GPL (tsm_unregister);
[Severity: High]
Can the tsm_dev->unregistering check in tsm_unregister() cause a use-after-free on tsm_dev when two callers race to unregister the same device?
tsm_register() initializes tsm_dev->dev with a single registration device reference. When a first caller enters tsm_unregister(), it holds down_write(&tsm_dev->registration_lock) across device_del(), tsm_wait_for_users(), and pci_tsm_unregister().
If a second caller enters tsm_unregister() while the first caller is waiting for users to drain, the second caller blocks in down_write(&tsm_dev->registration_lock) without holding a device reference on tsm_dev->dev. Once all user references have been dropped and the first caller finishes:
up_write(&tsm_dev->registration_lock); put_device(&tsm_dev->dev);
put_device(&tsm_dev->dev) drops the final reference and frees tsm_dev via tsm_release(), while the second caller wakes from down_write(&tsm_dev->registration_lock), reads tsm_dev->unregistering, and calls up_write(&tsm_dev->registration_lock) on the freed tsm_dev.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=15