From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8BAC0568FA0; Thu, 17 Sep 2026 14:02:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653763; cv=none; b=SQ1mHPwlaLDg1j/3ckoXY1dY1VpeaZwrabAiPuhctvZl/QEEclLDxChYaFNApAQep1l5cMsQfjOEmZxk0wNir/kiyCWpcxbNKEN8rezh7GGhE6Ji08R8ZEPzktSlkM2c8CnMqwXYLAL9BzhLTVVLPlG8+kxMd7KlsYR5+l/Q6cc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653763; c=relaxed/simple; bh=GX7IQ3vT8L0LNAuoC+pgThVbTafvtpub4+jtdSWGqqA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eHXTUBE4iHZjCSZh16IVuxmAv+ch/sFcewVz4R8n76pT72Z3MolwhktyJ5rYqbyM0BCeTrAomIdcw++V98xV55H5jB4pBxir/OP2unG/8qEA7FsxJhShvR+kbhyxOTxoS/S5H+BmL74+kn+mwHw3YGq+vhCWNYs0Rq6HybAC6KI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xtz0Q2pc; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xtz0Q2pc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 99CF81F00893; Thu, 17 Sep 2026 14:02:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653761; bh=m9zy1d1ZgB+XDZj5Vk9GF9b47gKzEvqTltamV4WC0h0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Xtz0Q2pcpvz7xj0crO1PaylcC5oj1p0GI/KClzrN0y1mpx3vXWhgTYr/3ZFYRW+jo p9FKXr0XVOxqn0qaXarrtuCkEIsmBLxYPJwKzOMYeQAOMTVhNdfSQv/HLmeNZ7gp47 sVWy/6gGa/0RtAW5//e3GTUtfxxn+sTj3Jv6feSG/tkEfF6nYNOIEPgKbg9fWJM6FM ijxdrnYxqscihyjmqXiGji8nUn0yCyaJ/uPIpcQP6XsYT9AA16MMszNHop5priGz4M i6XBfLZIckD5Q3Qn0gnUJwJxK8gReYJU9yclgmWgocUlUPwuxiTXWbVUbTk8ISnx9A KDTj3SowCxLng== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini , Anthony Krowiak Subject: [RFC PATCH v6 01/11] vfio: cache KVM VM file references instead of raw struct kvm pointers Date: Thu, 17 Sep 2026 19:31:49 +0530 Message-ID: <20260917140159.1163281-2-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" VFIO currently records struct kvm pointers on vfio_group, vfio_device_file and the opened vfio_device. Switch VFIO to track the VM's struct file instead, so VFIO and iommufd can use normal file references for VM lifetime instead of depending on KVM's internal struct kvm refcounting. KVM_CREATE_DEVICE binds the KVM VM lifetime to the KVM device fd lifetime. For KVM_DEV_TYPE_VFIO, the KVM VFIO device fd also takes references to each VFIO file added through KVM_DEV_VFIO_FILE_ADD. The KVM VFIO device fd therefore owns both the internal KVM reference and the VFIO file references in kvf->file. KVM_DEV_VFIO_FILE_ADD further installs the VM file association into the VFIO file. VFIO converts the struct kvm pointer to a VM file reference with get_file_active(&kvm->_file), because the KVM device fd can keep struct kvm alive after the original VM fd is already in final release. The association intentionally pins the VM file until KVM_DEV_VFIO_FILE_DEL or until the KVM VFIO device fd is released. This gives VFIO/iommufd a stable VM file reference source without taking a dependency on KVM's struct kvm lifetime. The KVM VFIO device release path clears the VFIO-side association before dropping its VFIO file references. When a VFIO device is opened or bound, VFIO takes an additional reference from the associated VM file and stores it in vfio_device::kvm_file for driver and iommufd use. That open-time reference is released from vfio_device_put_kvm() when the VFIO device is closed or unbound. This gives the ownership model: - KVM device fd pins struct kvm through kvm->users_count - KVM VFIO device fd pins VFIO files through kvf->file - VFIO group/device-file state pins the VM file while associated with KVM - vfio_device::kvm_file pins the VM file during active VFIO device use Acked-by: Anthony Krowiak Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/s390/crypto/vfio_ap_ops.c | 5 +- drivers/vfio/device_cdev.c | 10 ++-- drivers/vfio/group.c | 14 +++--- drivers/vfio/pci/vfio_pci_zdev.c | 7 +-- drivers/vfio/vfio.h | 16 ++++-- drivers/vfio/vfio_main.c | 81 ++++++++++++++++--------------- include/linux/kvm_host.h | 3 ++ include/linux/vfio.h | 17 ++++++- virt/kvm/kvm_main.c | 2 + 9 files changed, 91 insertions(+), 64 deletions(-) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_a= p_ops.c index 44b3a1dcc1b3..05996a8fd860 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -2054,11 +2054,12 @@ static int vfio_ap_mdev_open_device(struct vfio_dev= ice *vdev) { struct ap_matrix_mdev *matrix_mdev =3D container_of(vdev, struct ap_matrix_mdev, vdev); + struct kvm *kvm =3D vfio_device_get_kvm(vdev); =20 - if (!vdev->kvm) + if (!kvm) return -EINVAL; =20 - return vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm); + return vfio_ap_mdev_set_kvm(matrix_mdev, kvm); } =20 static void vfio_ap_mdev_close_device(struct vfio_device *vdev) diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c index 54abf312cf04..ca75ab8eb7bd 100644 --- a/drivers/vfio/device_cdev.c +++ b/drivers/vfio/device_cdev.c @@ -56,7 +56,7 @@ int vfio_device_fops_cdev_open(struct inode *inode, struc= t file *filep) static void vfio_df_get_kvm_safe(struct vfio_device_file *df) { spin_lock(&df->kvm_ref_lock); - vfio_device_get_kvm_safe(df->device, df->kvm); + vfio_device_get_kvm_safe(df->device, df->kvm_file); spin_unlock(&df->kvm_ref_lock); } =20 @@ -133,10 +133,10 @@ long vfio_df_ioctl_bind_iommufd(struct vfio_device_fi= le *df, } =20 /* - * Before the device open, get the KVM pointer currently - * associated with the device file (if there is) and obtain - * a reference. This reference is held until device closed. - * Save the pointer in the device for use by drivers. + * Before the device open, get the VM struct file currently + * associated with the device file (if there is one) and obtain a + * reference. This reference is held until the device is closed. + * Save the file in the device for use by drivers. */ vfio_df_get_kvm_safe(df); =20 diff --git a/drivers/vfio/group.c b/drivers/vfio/group.c index b2299e5bc6df..8950cfb9405d 100644 --- a/drivers/vfio/group.c +++ b/drivers/vfio/group.c @@ -163,7 +163,7 @@ static int vfio_group_ioctl_set_container(struct vfio_g= roup *group, static void vfio_device_group_get_kvm_safe(struct vfio_device *device) { spin_lock(&device->group->kvm_ref_lock); - vfio_device_get_kvm_safe(device, device->group->kvm); + vfio_device_get_kvm_safe(device, device->group->kvm_file); spin_unlock(&device->group->kvm_ref_lock); } =20 @@ -181,10 +181,10 @@ static int vfio_df_group_open(struct vfio_device_file= *df) mutex_lock(&device->dev_set->lock); =20 /* - * Before the first device open, get the KVM pointer currently - * associated with the group (if there is one) and obtain a reference - * now that will be held until the open_count reaches 0 again. Save - * the pointer in the device for use by drivers. + * Before the first device open, get the VM struct file currently + * associated with the group (if there is one) and obtain a + * reference now that will be held until the open_count reaches 0 + * again. Save the file in the device for use by drivers. */ if (device->open_count =3D=3D 0) vfio_device_group_get_kvm_safe(device); @@ -862,9 +862,7 @@ bool vfio_group_enforced_coherent(struct vfio_group *gr= oup) =20 void vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm) { - spin_lock(&group->kvm_ref_lock); - group->kvm =3D kvm; - spin_unlock(&group->kvm_ref_lock); + vfio_kvm_file_replace(&group->kvm_file, &group->kvm_ref_lock, kvm); } =20 /** diff --git a/drivers/vfio/pci/vfio_pci_zdev.c b/drivers/vfio/pci/vfio_pci_z= dev.c index 0990fdb146b7..a9d8e6aa3839 100644 --- a/drivers/vfio/pci/vfio_pci_zdev.c +++ b/drivers/vfio/pci/vfio_pci_zdev.c @@ -144,15 +144,16 @@ int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_= device *vdev, int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev) { struct zpci_dev *zdev =3D to_zpci(vdev->pdev); + struct kvm *kvm =3D vfio_device_get_kvm(&vdev->vdev); =20 if (!zdev) return -ENODEV; =20 - if (!vdev->vdev.kvm) + if (!kvm) return 0; =20 if (zpci_kvm_hook.kvm_register) - return zpci_kvm_hook.kvm_register(zdev, vdev->vdev.kvm); + return zpci_kvm_hook.kvm_register(zdev, kvm); =20 return -ENOENT; } @@ -161,7 +162,7 @@ void vfio_pci_zdev_close_device(struct vfio_pci_core_de= vice *vdev) { struct zpci_dev *zdev =3D to_zpci(vdev->pdev); =20 - if (!zdev || !vdev->vdev.kvm) + if (!zdev || !vfio_device_get_kvm(&vdev->vdev)) return; =20 if (zpci_kvm_hook.kvm_unregister) diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h index e4b72e79b7e3..41032104eb36 100644 --- a/drivers/vfio/vfio.h +++ b/drivers/vfio/vfio.h @@ -22,8 +22,8 @@ struct vfio_device_file { =20 u8 access_granted; u32 devid; /* only valid when iommufd is valid */ - spinlock_t kvm_ref_lock; /* protect kvm field */ - struct kvm *kvm; + spinlock_t kvm_ref_lock; /* protect kvm_file */ + struct file *kvm_file; struct iommufd_ctx *iommufd; /* protected by struct vfio_device_set::lock= */ }; =20 @@ -88,7 +88,7 @@ struct vfio_group { #endif enum vfio_group_type type; struct mutex group_lock; - struct kvm *kvm; + struct file *kvm_file; struct file *opened_file; struct iommufd_ctx *iommufd; spinlock_t kvm_ref_lock; @@ -434,11 +434,17 @@ static inline void vfio_virqfd_exit(void) #endif =20 #if IS_ENABLED(CONFIG_KVM) -void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm); +void vfio_kvm_file_replace(struct file **dst, spinlock_t *lock, struct kvm= *kvm); +void vfio_device_get_kvm_safe(struct vfio_device *device, struct file *kvm= _file); void vfio_device_put_kvm(struct vfio_device *device); #else +static inline void vfio_kvm_file_replace(struct file **dst, + spinlock_t *lock, struct kvm *kvm) +{ +} + static inline void vfio_device_get_kvm_safe(struct vfio_device *device, - struct kvm *kvm) + struct file *kvm_file) { } =20 diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c index ed538aebb0b8..fabff4aace1c 100644 --- a/drivers/vfio/vfio_main.c +++ b/drivers/vfio/vfio_main.c @@ -448,55 +448,61 @@ void vfio_unregister_group_dev(struct vfio_device *de= vice) EXPORT_SYMBOL_GPL(vfio_unregister_group_dev); =20 #if IS_ENABLED(CONFIG_KVM) -void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm) +void vfio_kvm_file_replace(struct file **dst, spinlock_t *lock, struct kvm= *kvm) { - void (*pfn)(struct kvm *kvm); - bool (*fn)(struct kvm *kvm); - bool ret; + struct file *old_kvm_file, *new_kvm_file =3D NULL; =20 - lockdep_assert_held(&device->dev_set->lock); + /* + * @kvm can outlive the VM fd and its final __fput(). Only take a + * new reference if the VM file is still active. + */ + if (kvm) + new_kvm_file =3D get_file_active(&kvm->_file); =20 - if (!kvm) - return; + spin_lock(lock); + old_kvm_file =3D *dst; + *dst =3D new_kvm_file; + spin_unlock(lock); =20 - pfn =3D symbol_get(kvm_put_kvm); - if (WARN_ON(!pfn)) - return; + if (old_kvm_file) + fput(old_kvm_file); +} =20 - fn =3D symbol_get(kvm_get_kvm_safe); - if (WARN_ON(!fn)) { - symbol_put(kvm_put_kvm); - return; - } +void vfio_device_get_kvm_safe(struct vfio_device *device, struct file *kvm= _file) +{ + lockdep_assert_held(&device->dev_set->lock); =20 - ret =3D fn(kvm); - symbol_put(kvm_get_kvm_safe); - if (!ret) { - symbol_put(kvm_put_kvm); - return; - } + /* + * Take a VM file reference if the KVM fd is still active. + */ + if (kvm_file) + kvm_file =3D get_file(kvm_file); =20 - device->put_kvm =3D pfn; - device->kvm =3D kvm; + device->kvm_file =3D kvm_file; } =20 void vfio_device_put_kvm(struct vfio_device *device) { + struct file *kvm_file; + lockdep_assert_held(&device->dev_set->lock); =20 - if (!device->kvm) + kvm_file =3D device->kvm_file; + if (!kvm_file) return; =20 - if (WARN_ON(!device->put_kvm)) - goto clear; + device->kvm_file =3D NULL; + fput(kvm_file); +} =20 - device->put_kvm(device->kvm); - device->put_kvm =3D NULL; - symbol_put(kvm_put_kvm); +struct kvm *vfio_device_get_kvm(struct vfio_device *device) +{ + if (!device->kvm_file) + return NULL; =20 -clear: - device->kvm =3D NULL; + return device->kvm_file->private_data; } +EXPORT_SYMBOL_GPL(vfio_device_get_kvm); #endif =20 /* true if the vfio_device has open_device() called but not close_device()= */ @@ -1525,13 +1531,10 @@ static void vfio_device_file_set_kvm(struct file *f= ile, struct kvm *kvm) struct vfio_device_file *df =3D file->private_data; =20 /* - * The kvm is first recorded in the vfio_device_file, and will - * be propagated to vfio_device::kvm when the file is bound to - * iommufd successfully in the vfio device cdev path. + * Cache the VM file reference associated with this VFIO file so it + * can be pinned into vfio_device while the device is open. */ - spin_lock(&df->kvm_ref_lock); - df->kvm =3D kvm; - spin_unlock(&df->kvm_ref_lock); + vfio_kvm_file_replace(&df->kvm_file, &df->kvm_ref_lock, kvm); } =20 /** @@ -1539,8 +1542,8 @@ static void vfio_device_file_set_kvm(struct file *fil= e, struct kvm *kvm) * @file: VFIO group file or VFIO device file * @kvm: KVM to link * - * When a VFIO device is first opened the KVM will be available in - * device->kvm if one was associated with the file. + * When a VFIO device is first opened, VFIO caches a VM file reference if + * one was associated with the file. */ void vfio_file_set_kvm(struct file *file, struct kvm *kvm) { diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 9c1cf1a6559e..b3b0fa7d53a2 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -45,6 +45,8 @@ #include #include =20 +struct file; + #ifndef KVM_MAX_VCPU_IDS #define KVM_MAX_VCPU_IDS KVM_MAX_VCPUS #endif @@ -840,6 +842,7 @@ struct kvm { struct srcu_struct srcu; struct srcu_struct irq_srcu; pid_t userspace_pid; + struct file __rcu *_file; bool override_halt_poll_ns; unsigned int max_halt_poll_ns; u32 dirty_ring_size; diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 31b826efba00..bca1d00f7845 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -22,8 +22,22 @@ struct kvm; struct iommufd_ctx; struct iommufd_device; struct iommufd_access; +struct vfio_device; struct vfio_info_cap; =20 +#if IS_ENABLED(CONFIG_KVM) +/* + * Return the KVM associated with @vdev's kvm_file. The returned pointer + * is valid only while VFIO device open holds the kvm_file reference. + */ +struct kvm *vfio_device_get_kvm(struct vfio_device *vdev); +#else +static inline struct kvm *vfio_device_get_kvm(struct vfio_device *vdev) +{ + return NULL; +} +#endif + /* * VFIO devices can be placed in a set, this allows all devices to share t= his * structure and the VFIO core will provide a lock that is held around @@ -54,7 +68,7 @@ struct vfio_device { struct list_head dev_set_list; unsigned int migration_flags; u8 precopy_info_v2; - struct kvm *kvm; + struct file *kvm_file; =20 /* Members below here are private, not for driver use */ unsigned int index; @@ -66,7 +80,6 @@ struct vfio_device { unsigned int open_count; struct completion comp; struct iommufd_access *iommufd_access; - void (*put_kvm)(struct kvm *kvm); struct inode *inode; #if IS_ENABLED(CONFIG_IOMMUFD) struct iommufd_device *iommufd_device; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 46e92b5dc380..ba39e3857e7d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1369,6 +1369,7 @@ static int kvm_vm_release(struct inode *inode, struct= file *filp) =20 kvm_irqfd_release(kvm); =20 + RCU_INIT_POINTER(kvm->_file, NULL); kvm_put_kvm(kvm); return 0; } @@ -5550,6 +5551,7 @@ static int kvm_dev_ioctl_create_vm(unsigned long type) r =3D PTR_ERR(file); goto put_kvm; } + rcu_assign_pointer(kvm->_file, file); =20 /* * Don't call kvm_put_kvm anymore at this point; file->f_op is --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E43F4565107; Thu, 17 Sep 2026 14:02:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653772; cv=none; b=XIOoULzbpC5sVN9OE4CgmSt6g3InxpSSr4GJI1JlhMSUbAt1O3cLgeXKYAeEwpRASASqlr6U9psau4juQLDyLSpVUFbebtpL4O1ysOmYy21Fd8MMY96/L+ppBXPMSOnt8LmQIiehEoSzV5l+vW5Lymv+/MAFYag3hdI7bz4L3ls= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653772; c=relaxed/simple; bh=U3ruNC+aTwd2NW/FLlc8CFgc0Kc8+hGMcpCLR0Ta6n0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VMTuZJKRclCM1A7Lnx8W+VP7T+DvV6h+argmMaiP1fvuShVmkwfm4N9PyphFfIPhsDXfsBIUepSEDtsa2RlhO8Y85/FDP2W7sCUfKALI60FnzrrzKm+W4uIzqUy+1gIE2bVWAiXyI53gvQneQsS1UTt2OnqHWcEvQWxtlVp0ndI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=E+VunlDT; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="E+VunlDT" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2BFAC1F000FF; Thu, 17 Sep 2026 14:02:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653770; bh=rLwCfcv0m3AZLpWzVTe2rRR7huQPS33UjInoD8KSQpQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=E+VunlDTMYfLzIvK7X0cB2DuzHaBCrMFHhGAIEFU95gPOPbzfTM16Eokw8G5Vvgsu p31m8Q3vhXusdzvS5uObMdUx+02+fmNYKEGsFOhOCEj/Iagx9+HMLkxnc6sldPTwsK GzWMhIaA52Ib5SCFV8ULBSHuCRYeOzSMja2gQ/YzouOnUGnzlPYmlFf+KY9ofjmXC9 z8odUohG8kw/Pyj5Z3nGhl6b137V2aoIaX5ka6ty6st5756/wDq/Lo4V9hAmKzu1cf p6qYpfZqJ/g54CUfIjhRedNhI4y74A9jRWNi17ZIfaQozoTps1GysMM9Dk8R24du1I HmTCq7SbdGy/Q== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 02/11] vfio: cdev: Reject duplicate bind before updating KVM file Date: Thu, 17 Sep 2026 19:31:50 +0530 Message-ID: <20260917140159.1163281-3-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The cdev path only supports one bound/open device fd, but VFIO_DEVICE_BIND_IOMMUFD only checked the per-file access_granted flag before capturing the KVM file reference. A second fd for the same device could therefore replace device->kvm_file, fail later in vfio_df_open() because open_count is already nonzero, and then clear the active KVM association during error cleanup. Reject the bind while holding dev_set->lock if the device is already open, matching the existing cdev single-open rule before touching the device-wide KVM state. Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/vfio/device_cdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c index ca75ab8eb7bd..67e48f7ebfc3 100644 --- a/drivers/vfio/device_cdev.c +++ b/drivers/vfio/device_cdev.c @@ -115,8 +115,8 @@ long vfio_df_ioctl_bind_iommufd(struct vfio_device_file= *df, return ret; =20 mutex_lock(&device->dev_set->lock); - /* one device cannot be bound twice */ - if (df->access_granted) { + /* The cdev path only supports one bound/open device fd. */ + if (df->access_granted || device->open_count) { ret =3D -EINVAL; goto out_unlock; } --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8683357C706; Thu, 17 Sep 2026 14:03:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653781; cv=none; b=QSjDckB336VTC+/5UoKesUlGOb3DNQT/gLOCXQb+KC4f/Gg6rSp3pI0TdorVibFoiJEIc9bkEmGIgRzavVKVzQ1fHF1BbanAW7W4glVKlyAqlNG2gk3O4rHDJI97G1Y3mKWpVBKmsYMS8m7OO91Wq2n5g4kU7iuhkjIWtxonRKs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653781; c=relaxed/simple; bh=NOCtlZPZklY5YeSVnTqQ27iIEYvSrZ6UN7JZPDkTzbk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AYh3eFKwTpS9nV9H9NTTFhsSZtaonX1fyXql2uzS2jmkA9hfbBckARwQ+sRI0hF1HGQPkLzg2Dwi+YL/XA0TdhYmqOEbZsbn6Vdrx8Qahe9uN/JBuxhr5XRvlytxt7UXoK8XK7IAB2c0l8Qn1z9M7a+BBik36iCQyZRNpZNTpX4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jsAyS/31; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jsAyS/31" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 55D4E1F00893; Thu, 17 Sep 2026 14:02:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653780; bh=jPOre9bkQOZnirJHiSRGJjgxkloPfu9X/afpRcHUTPc=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=jsAyS/31fHSGYrUluZbJX2mtU6nVYzVqsJ2SwYci6NNFso8vY37+CvBAstC7YSk/g mEUYplWrMlP0E0jBJcbMZGCp05kfcJZczvtbk5P8clxxYLXtTFZDbymWZLgryUu+ad 7+BiYahpVmqlRENWXJusGYGvfuEhuXeZv3fxVlbv83ixVg5yfXUaMPXfRl3JGiYSht rWCJANeuUq86mZF5WB1AI2GdApRx7NdnnY6yIzSoOOQg9C1DWKdenDmgF0LD5vbpua vY9NrfZewkLSBiHCLbGzD+ZWRDKr4T+9OctZfol7YdqBqHhogZQRVEt9bp8qgx5giz 0NM248jgXfpjw== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini , Jason Gunthorpe Subject: [RFC PATCH v6 03/11] iommufd/device: Associate KVM file pointer with iommufd_device Date: Thu, 17 Sep 2026 19:31:51 +0530 Message-ID: <20260917140159.1163281-4-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Shameer Kolothum 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. Signed-off-by: Shameer Kolothum Reviewed-by: Jason Gunthorpe [nicolinc: fix build error in iommufd_test_mock_domain()] Signed-off-by: Nicolin Chen [aneesh.kumar: Switch to use kvm_file] Signed-off-by: Aneesh Kumar K.V (Arm) --- 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(-) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 170a7005f0bc..718abdc0e627 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -203,6 +203,7 @@ void iommufd_device_destroy(struct iommufd_object *obj) * 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 @@ -216,7 +217,9 @@ void iommufd_device_destroy(struct iommufd_object *obj) * 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; struct iommufd_group *igroup; @@ -266,6 +269,8 @@ struct iommufd_device *iommufd_device_bind(struct iommu= fd_ctx *ictx, if (!iommufd_selftest_is_mock_dev(dev)) iommufd_ctx_get(ictx); idev->dev =3D dev; + /* reference is already taken in vfio_df_ioctl_bind_iommufd() */ + idev->kvm_file =3D kvm_file; idev->enforce_cache_coherency =3D device_iommu_capable(dev, IOMMU_CAP_ENFORCE_CACHE_COHERENCY); /* The calling driver is a user until iommufd_device_unbind() */ diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommuf= d/iommufd_private.h index 43fbc5bed8de..6cf76f7ca379 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -488,6 +488,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; diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selft= est.c index af07c642a526..a193390f9d07 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -1069,7 +1069,7 @@ static int iommufd_test_mock_domain(struct iommufd_uc= md *ucmd, goto out_sobj; } =20 - idev =3D iommufd_device_bind(ucmd->ictx, &sobj->idev.mock_dev->dev, + idev =3D iommufd_device_bind(ucmd->ictx, &sobj->idev.mock_dev->dev, NULL, &idev_id); if (IS_ERR(idev)) { rc =3D PTR_ERR(idev); diff --git a/drivers/vfio/iommufd.c b/drivers/vfio/iommufd.c index a38d262c6028..d2d0bd9382a1 100644 --- a/drivers/vfio/iommufd.c +++ b/drivers/vfio/iommufd.c @@ -119,7 +119,8 @@ int vfio_iommufd_physical_bind(struct vfio_device *vdev, { struct iommufd_device *idev; =20 - idev =3D iommufd_device_bind(ictx, vdev->dev, out_device_id); + idev =3D iommufd_device_bind(ictx, vdev->dev, vdev->kvm_file, + out_device_id); if (IS_ERR(idev)) return PTR_ERR(idev); vdev->iommufd_device =3D idev; diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h index 6e7efe83bc5d..0a0bb4abfbd2 100644 --- a/include/linux/iommufd.h +++ b/include/linux/iommufd.h @@ -59,7 +59,9 @@ struct iommufd_object { }; =20 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); =20 int iommufd_device_attach(struct iommufd_device *idev, ioasid_t pasid, --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 477AC57C715; Thu, 17 Sep 2026 14:03:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653792; cv=none; b=r425y4BddfUnL1WJcBB5j7wiWJolhpVlxOqlvnjF6sIVnyvNv/WXivj0HeGwrzUuDX5zabAI2XPyU+b7AmhuxwcRDIJPvoS2BoG7T4iyEYKtaIaFBnpz9FYXf3Jthq9tdqcUUT+NGg1bIyu+sLev1qfZCUC8yP1/NQDToBkXWWQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653792; c=relaxed/simple; bh=YZtKoM/a/CTrNr/NXer57PB2adeZTwdJ8R2O9Ner4K8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iM6m4Q18QgpUMOH3lTIUK68RpXl2NJgi4X0dCJH8lOyEO209sBOkIG0FBNTClPpR7ZxDT1CATGCLMHq+aDGP/hj8th5O9q6JqvGu25tv2+XrzM36jO+uIi7olPJ7k5jajd6lD1nji9UNFMsNQY+MpuzGSEnS6Euu4/75oJCSfsc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dRWob39+; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dRWob39+" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 423901F000FF; Thu, 17 Sep 2026 14:03:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653789; bh=7tyAQCrPqe5cIWkqIQCLhlpGkaY0VQtDzrIlPeSjyt4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=dRWob39+RraIzMlZG3LBxOO2hKuUT7THDy8C0/wJ89Yywu1x0HlCbt4lOaZIdAZQn ltEf9WHXYocjXLrOTfTrU0qeJ8EKDxlLGRu1wN0yB8du85E2pYhC0Eut5qYau3Tyyv X5XhABAG8tp/hlGGfPVQeGz8ZUFiMwL10g0RkWJm/p4X9rOfHf9Q2YQJEgiDivA70/ vSip73qkiTsA6r2Ygavempb2x6hUTzRzRajK35XKU9PHr5mUp1iq02sCXKuABlqQxo rnwa3IeyDlX8lz2+bWLA4Nt2yuKrp8gEyQ/gARu8VijemMA2rw7jWwhrm+R2+NPPFg C+70w6J85sdAA== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 04/11] iommufd/viommu: Keep a reference to the KVM file Date: Thu, 17 Sep 2026 19:31:52 +0530 Message-ID: <20260917140159.1163281-5-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Nicolin Chen 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. Based on an original patch by Shameer Kolothum [nicolinc: hold kvm's users_count] Signed-off-by: Nicolin Chen [aneesh.kumar: Switch to use kvm_file] Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/iommu/iommufd/viommu.c | 5 +++++ include/linux/iommufd.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index 4081deda9b33..bf5d58d55939 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES */ +#include #include "iommufd_private.h" =20 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); } =20 @@ -76,6 +79,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) } =20 xa_init(&viommu->vdevs); + if (idev->kvm_file) + viommu->kvm_file =3D get_file(idev->kvm_file); viommu->type =3D cmd->type; viommu->ictx =3D ucmd->ictx; viommu->hwpt =3D hwpt_paging; diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h index 0a0bb4abfbd2..3267717f676d 100644 --- a/include/linux/iommufd.h +++ b/include/linux/iommufd.h @@ -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; =20 const struct iommufd_viommu_ops *ops; =20 --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8280A56E05D; Thu, 17 Sep 2026 14:03:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653799; cv=none; b=UF7UhkMfSkSTbAe4+YadyRLgxCZL+ycS3Hq8k/16vr8bceHu0xsIr+pSqLonXbXZnjR+KJMpnRX91n1JW/ZwhOPwrIdQbZS2N8A/K7CxpTcAsDx6Fu1wmIEG9caEYharIS1lhPuj4wmo3KujHJ4pU077vYg/8PM02NAOVlLa+WM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653799; c=relaxed/simple; bh=14Js+dHOD1nUBg/odeDxXKAu4rbFwEKOFsL+RT7+odU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OjDv2byPXaUQmlNtWJ6mgyEauvXooJMHR1wieoWJRM89HkPWbdtE+ISvMBsal6ZnPzV54Om1E4o7uEhheQWHsybBt0KJ24OlwU5z3ZTMCunw+9nP3LBZ3EJKJkDe2vnLoB/u/NbyOD/KGa1qyyEkuOZxZJVOm2RXySZJLDXulms= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EMWO7rI8; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EMWO7rI8" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4C9941F00893; Thu, 17 Sep 2026 14:03:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653798; bh=j++GxayTN2VHprz5XrBEAWC/QN1HnZASp2sGP+7sISQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=EMWO7rI8UUV355gbOkwO/E7b7Pbh7IPsL957TYRtX05nGQ+B35OYDFHo/L9bO4hl3 e5GTaj2mLAJpjCVd+/Flh7SaBYeYQORDveOXZdXTh9kFIkdDdwJ+bRjZWxeyy549ph 8b8Dr51BDVAx5aDpWBjwcVqDJDcCRHapql/Wbwkp+GT9aZa9nkgS9PNWqin6lojS9c qEKXC8ZyASEzQWhymufnZn3I6vX2NeWBE1cHX+Dgd/1a1TYldv/MBXjkpNYFWwJ5F+ UYVNZeEGhDi0QJCV9qq57vwWw7uRzPHXpRzhXNfDTQqQ/pKaW2+K49pyesNQ1SHrrO pt7gHQxrsjvUA== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 05/11] iommu: Add a helper to validate a vIOMMU parent Date: Thu, 17 Sep 2026 19:31:53 +0530 Message-ID: <20260917140159.1163281-6-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Let a vIOMMU implementation ask the physical IOMMU driver whether a device and nesting parent are compatible. Return EOPNOTSUPP when the driver does not supply the validation callback. Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/iommu/iommu.c | 11 +++++++++++ include/linux/iommu.h | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index e8f13dcebbde..04cd5a686e18 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -47,6 +47,17 @@ static unsigned int iommu_def_domain_type __read_mostly; static bool iommu_dma_strict __read_mostly =3D IS_ENABLED(CONFIG_IOMMU_DEF= AULT_DMA_STRICT); static u32 iommu_cmd_line __read_mostly; =20 +int iommu_viommu_validate_parent(struct device *dev, + enum iommu_viommu_type type, struct iommu_domain *parent_domain) +{ + const struct iommu_ops *ops =3D dev_iommu_ops(dev); + + if (!ops->viommu_validate_parent) + return -EOPNOTSUPP; + return ops->viommu_validate_parent(dev, type, parent_domain); +} +EXPORT_SYMBOL_GPL(iommu_viommu_validate_parent); + /* Tags used with xa_tag_pointer() in group->pasid_array */ enum { IOMMU_PASID_ARRAY_DOMAIN =3D 0, IOMMU_PASID_ARRAY_HANDLE =3D 1 }; =20 diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d20aa6f6863a..ee016a7c7a41 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -435,6 +435,9 @@ struct iommu_user_data { size_t len; }; =20 +int iommu_viommu_validate_parent(struct device *dev, + enum iommu_viommu_type type, struct iommu_domain *parent_domain); + /** * struct iommu_user_data_array - iommu driver specific user space data ar= ray * @type: The data type of all the entries in the user buffer array @@ -679,6 +682,8 @@ __iommu_copy_struct_to_user(const struct iommu_user_dat= a *dst_data, * resources shared/passed to user space IOMMU instance. Ass= ociate * it with a nesting @parent_domain. It is required for driv= er to * set @viommu->ops pointing to its own viommu_ops + * @viommu_validate_parent: Validate that @parent_domain is compatible with + * @dev for @viommu_type. * @owner: Driver module providing these ops * @identity_domain: An always available, always attachable identity * translation. @@ -734,6 +739,9 @@ struct iommu_ops { int (*viommu_init)(struct iommufd_viommu *viommu, struct iommu_domain *parent_domain, const struct iommu_user_data *user_data); + int (*viommu_validate_parent)(struct device *dev, + enum iommu_viommu_type viommu_type, + struct iommu_domain *parent_domain); =20 const struct iommu_domain_ops *default_domain_ops; struct module *owner; --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CEC4E57D213; Thu, 17 Sep 2026 14:03:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653811; cv=none; b=ZLwEYwrepcLPGow8aM/z+v1Yf36fFdKOhyPfHs8f1LR7sM+lf2JBg86BUemaFMwpUfSJkiLsCwV6q2JPTm1BynCPfExjgp4C18vWJs5hVp8il/iQfqf9+9qvC8hF0V40tCSgCuJNI84tX/MR9nxqftOf0xq1/Gsz7cXQKrIrjIY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653811; c=relaxed/simple; bh=CRlkuVSNCL+K3guGa60sK05FvrUBw5E/lQqAmg8TnEs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=n7WYwiPEge8qgfCL4sDG/wyJ9jS041mjo/yNAhL2PzQsR0cNL3BBUQ648v3AUC9HLA+ihM/lNuJO6NpPdmwJ1g4V/Mt/rQFUmx754SL6+08GcIp+OuswopU4UwJK66AdWHL2eMqJZQV92dH3dZPk+vEOu+3YtzUZe0igeTGAe2A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LNqaNV0U; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="LNqaNV0U" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 045DE1F000FF; Thu, 17 Sep 2026 14:03:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653807; bh=kYPOci51J9ymg5YOitMfoWSrOdVs2KNRRIQ8zCl/hJo=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=LNqaNV0Ut43iYq70CLaSD0ZTt0bYFypaQi3zO8BVNPfytGo5DCwjoHUqagLLW1p3W jQuVm2mHopBer9//guyT8FmtTCAsLCXebfZjxjqa7EY3KD5MgJhIittg+z2CanMX3a UDqd74eyCrDjsHK4lio6MEvPXjVZc3yy4SFF7DF0nHW0hd1c49wCljBSv7TBHDHenj /XrvSrUC9NX+rG9RikHAFfifAtL4WGl6lO6JfJkNF3NYPk0S1KHTjGeF8yzgCC1ccv oqw/wrm1AHB1eA8Rap9bLgje2ejDimM6H6zi9AMrCclS3ZySsdIU4pTzYAWh1MS0iw bVSnpO0v3DRTg== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 06/11] iommu: Add a helper to query vIOMMU hardware parameters Date: Thu, 17 Sep 2026 19:31:54 +0530 Message-ID: <20260917140159.1163281-7-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose a type-specific kernel parameter query through the physical IOMMU driver. This lets an external vIOMMU provider obtain hardware parameters without depending on private driver structures. Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/iommu/iommu.c | 11 +++++++++++ include/linux/iommu.h | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 04cd5a686e18..70ca10380742 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -58,6 +58,17 @@ int iommu_viommu_validate_parent(struct device *dev, } EXPORT_SYMBOL_GPL(iommu_viommu_validate_parent); =20 +int iommu_viommu_get_params(struct device *dev, + enum iommu_viommu_type type, void *params, size_t params_size) +{ + const struct iommu_ops *ops =3D dev_iommu_ops(dev); + + if (!ops->viommu_get_params) + return -EOPNOTSUPP; + return ops->viommu_get_params(dev, type, params, params_size); +} +EXPORT_SYMBOL_GPL(iommu_viommu_get_params); + /* Tags used with xa_tag_pointer() in group->pasid_array */ enum { IOMMU_PASID_ARRAY_DOMAIN =3D 0, IOMMU_PASID_ARRAY_HANDLE =3D 1 }; =20 diff --git a/include/linux/iommu.h b/include/linux/iommu.h index ee016a7c7a41..c006478c4b04 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -437,6 +437,8 @@ struct iommu_user_data { =20 int iommu_viommu_validate_parent(struct device *dev, enum iommu_viommu_type type, struct iommu_domain *parent_domain); +int iommu_viommu_get_params(struct device *dev, + enum iommu_viommu_type type, void *params, size_t params_size); =20 /** * struct iommu_user_data_array - iommu driver specific user space data ar= ray @@ -684,6 +686,8 @@ __iommu_copy_struct_to_user(const struct iommu_user_dat= a *dst_data, * set @viommu->ops pointing to its own viommu_ops * @viommu_validate_parent: Validate that @parent_domain is compatible with * @dev for @viommu_type. + * @viommu_get_params: Fill a kernel buffer with parameters for @viommu_ty= pe. + * The buffer format is defined by that vIOMMU type. * @owner: Driver module providing these ops * @identity_domain: An always available, always attachable identity * translation. @@ -742,6 +746,9 @@ struct iommu_ops { int (*viommu_validate_parent)(struct device *dev, enum iommu_viommu_type viommu_type, struct iommu_domain *parent_domain); + int (*viommu_get_params)(struct device *dev, + enum iommu_viommu_type viommu_type, + void *params, size_t params_size); =20 const struct iommu_domain_ops *default_domain_ops; struct module *owner; --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6667057D23B; Thu, 17 Sep 2026 14:03:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653818; cv=none; b=fVKepAH1o1FnJgmXpwVyABjlZzFEDBwqjlrR4iB7CgpHOw/j/U5ig9GkfAe3dFLA5xh1xhXNLVAUfySU7Ul4Kbr5/wapjxPRPCGn2quJlOdh/yAxMlImOyo0EVCSTTzuHTSsBfGoA9zkLZRuSKhPnTtsSihHrnqRZNfovlF2OWI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653818; c=relaxed/simple; bh=IpHMT6b+XPPyXbR/Ayv8VIzPvMbSuBfwmaINon0duNo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DvEK39Ad+2JCiKq53l538GVzf8xH87iBFsFTKzrfGO4ZudTin1HmOoTyKCwQkOwBAd8BZalQ67AaKi/EWv1x2AJALwV+o5MWv+SgC+q07zBsX1jTR+Nn7xwX2BT9qY+0IJKfSzDwZkt8CKmQLKuRHMN/+EdRZEy61Fd7YlNL3ng= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kD2sMs+z; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kD2sMs+z" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 207461F00899; Thu, 17 Sep 2026 14:03:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653815; bh=y17eDcxnxNk5lpH1HWpqAW8JDmaAMUUrW6aPIX1kJNA=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=kD2sMs+z2pkAdSh1HbXZZTmxIP+0Z+7pQ7of47WOAzkGrmonmMo7KAg7uWUspMAK+ Z1+pahX2OBVm5iJ2nl8+QQlUHOadWmq7JMuJTxT06kY+jCHnVB6RndfrvwToLwSM6C qkQ2Lo9XUuZTxYPSq1ugsKfNuDd65jXATEWmnJESZ+VinvXJZ8nhDUgWau3vKdGsL8 T99xro347erdAojeWilx1l7NJRmx8HOX1ubiI5Xj1byByvXQ7D2Tn9P1BQ30cyIYFw L+OpWmqsXlCCtlkr9DCWcj22DVhczGymdQuIHKUeoo9LccCtlG9vYqTb5ZyD5whgYT Etd3YWpsNMyOw== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 07/11] coco: tsm: Expose active-user lifetime references Date: Thu, 17 Sep 2026 19:31:55 +0530 Message-ID: <20260917140159.1163281-8-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add separate active-user references for consumers that require both the TSM device and its PCI/TSM resources. Initialize the users count to one when allocating the TSM device. This initial reference represents the registration and keeps the PCI/TSM resources registered until tsm_unregister() drops it. tsm_get() takes both an active-user reference and a device reference. tsm_put() drops the active-user reference first, allowing the last active user to tear down PCI/TSM resources while struct tsm_dev and the driver operations remain valid, and then drops the device reference. tsm_unregister() drops the initial registration reference and unregisters the device. Existing active users retain the PCI/TSM resources, while their device references keep struct tsm_dev alive. The final active-user reference tears down the PCI/TSM resources, independently of the final device reference releasing struct tsm_dev. Restructure PCI/TSM registration error handling so tsm_register() removes the device and lets its scoped device reference perform the final release. Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/virt/coco/tsm-core.c | 55 ++++++++++++++++++++++++++++++------ include/linux/tsm.h | 13 +++++++++ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/drivers/virt/coco/tsm-core.c b/drivers/virt/coco/tsm-core.c index 0843b77c6549..90d304d55b59 100644 --- a/drivers/virt/coco/tsm-core.c +++ b/drivers/virt/coco/tsm-core.c @@ -57,6 +57,39 @@ static const struct class tsm_class =3D { .dev_groups =3D tsm_pci_groups, }; static DEFINE_IDA(tsm_ida); +static void tsm_put_active(struct tsm_dev *tsm_dev) +{ + if (!refcount_dec_and_test(&tsm_dev->users)) + return; + if (tsm_dev->pci_ops) + pci_tsm_unregister(tsm_dev); +} + +/** + * tsm_get() - Take an active reference to a TSM + * @tsm_dev: registered TSM or TSM already held by an active reference + * + * Keeps both the device and its PCI/TSM resources alive. + */ +void tsm_get(struct tsm_dev *tsm_dev) +{ + get_device(&tsm_dev->dev); + refcount_inc(&tsm_dev->users); +} +EXPORT_SYMBOL_GPL(tsm_get); + +/** + * tsm_put() - Release an active TSM reference + * @tsm_dev: TSM acquired with tsm_get() + * + * The last active reference tears down PCI/TSM resources after unregister. + */ +void tsm_put(struct tsm_dev *tsm_dev) +{ + tsm_put_active(tsm_dev); + put_device(&tsm_dev->dev); +} +EXPORT_SYMBOL_GPL(tsm_put); =20 static int match_id(struct device *dev, const void *data) { @@ -90,6 +123,7 @@ static struct tsm_dev *alloc_tsm_dev(struct device *pare= nt) return ERR_PTR(id); =20 tsm_dev->id =3D id; + refcount_set(&tsm_dev->users, 1); dev =3D &tsm_dev->dev; dev->parent =3D parent; dev->class =3D &tsm_class; @@ -98,27 +132,26 @@ static struct tsm_dev *alloc_tsm_dev(struct device *pa= rent) return no_free_ptr(tsm_dev); } =20 -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 *p= ci_ops) { int rc; =20 if (!pci_ops) - return tsm_dev; + return 0; =20 tsm_dev->pci_ops =3D pci_ops; rc =3D pci_tsm_register(tsm_dev); if (rc) { + tsm_dev->pci_ops =3D 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; } sysfs_update_group(&tsm_dev->dev.kobj, &tsm_pci_group); =20 /* Notify TSM userspace that PCI/TSM operations are now possible */ kobject_uevent(&tsm_dev->dev.kobj, KOBJ_CHANGE); - return tsm_dev; + return 0; } =20 struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pc= i_ops) @@ -139,14 +172,18 @@ struct tsm_dev *tsm_register(struct device *parent, s= truct pci_tsm_ops *pci_ops) if (rc) return ERR_PTR(rc); =20 - return tsm_register_pci_or_reset(no_free_ptr(tsm_dev), pci_ops); + rc =3D 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); =20 void tsm_unregister(struct tsm_dev *tsm_dev) { - if (tsm_dev->pci_ops) - pci_tsm_unregister(tsm_dev); + tsm_put_active(tsm_dev); device_unregister(&tsm_dev->dev); } EXPORT_SYMBOL_GPL(tsm_unregister); diff --git a/include/linux/tsm.h b/include/linux/tsm.h index 7f72a154b6b2..f38d6fcf9cc9 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -2,6 +2,7 @@ #ifndef __TSM_H #define __TSM_H =20 +#include #include #include #include @@ -109,8 +110,18 @@ struct tsm_report_ops { }; =20 struct pci_tsm_ops; + +/** + * struct tsm_dev - TEE Security Manager device + * @dev: device-model representation of the TSM + * @users: registration reference plus active references that retain the + * PCI/TSM resources; reaching zero tears down those resources + * @id: instance identifier + * @pci_ops: PCI/TSM operations, or %NULL when PCI/TSM is unsupported + */ struct tsm_dev { struct device dev; + refcount_t users; int id; const struct pci_tsm_ops *pci_ops; }; @@ -122,6 +133,8 @@ int tsm_report_register(const struct tsm_report_ops *op= s, void *priv); int tsm_report_unregister(const struct tsm_report_ops *ops); struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *op= s); void tsm_unregister(struct tsm_dev *tsm_dev); +void tsm_get(struct tsm_dev *tsm_dev); +void tsm_put(struct tsm_dev *tsm_dev); struct tsm_dev *find_tsm_dev(int id); struct pci_ide; int tsm_ide_stream_register(struct pci_ide *ide); --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 14594566C40; Thu, 17 Sep 2026 14:03:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653826; cv=none; b=iE3MWmTuz/yfKmzGvGSElBMGcHYGUrey3Ku1Bap/4IR2U4pGboQdPzoOGhkOyeWC26TLB6n8FHO0WGQAmUiQCxXnzwU18yxKMGFU0k5i3QeP6owEhFixjj6ZArvCNcG0pW/j9KilZGfgqZwMrGj4l23fPV1N29qFRVpfi9FwvW8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653826; c=relaxed/simple; bh=AiXUXEYeI3dv0Ek2R70toPCfHxO8CZdfF/oaLg7m5Yk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hhifI/Crx32tsUOPv6nvetw9yxq2FL6SSUkwSkcI2x+nP2W17Nnfq1Fti2ZxS5JMFeUR8FDJt/zXIgbW6XZdDxCx8RWDkRLFsUuMU8KJF338KadoIWM6rT8s3BzHXZ2zXV9yUZ7FvheN5AZ2aTFR2bnY1wTlgv7xeUMyZxnQV+A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ayY7wVnl; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ayY7wVnl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B46AB1F000FF; Thu, 17 Sep 2026 14:03:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653824; bh=13baf1rjLpIrQ0sHp8XANd8ER0YUCiRI5yzi0IA1IIk=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=ayY7wVnlF++tHoBBimXiUGw+58KfCgx/uwfSzf4Yp1/gpVVSaeqan0P5HGHG+oNst SI+TMfdfx1q9ve+G1BF9nNnc8VTAfuU9t4+Fx5LW/jjQkxCHW6h5MOjbYJdnoKNiOG i822/GoHEApcjwBEp22UhnK7P0YkctTuCv0YwrNYSCgf+Iuy4kRrn9V1MGvZ0qMZJ0 0NhG92NUqAwsGtbsBTM0aobWEckIKsXNYNaLB7G3oLEnCj5a0woH9fmpuPSuhRZ7ut L+uy7Uu53BR8BT3GH9UsjmqzejsFjBM4cmGKakZu/VmW2fNd1IXXdoxMixB4Td0HJM zLhFIVP2D5T6Q== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 08/11] iommufd: Add vIOMMU provider support Date: Thu, 17 Sep 2026 19:31:56 +0530 Message-ID: <20260917140159.1163281-9-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" vIOMMU creation currently dispatches directly through the physical IOMMU driver. Some vIOMMU types need an implementation supplied by another subsystem, together with private data whose lifetime extends across all vIOMMUs using that implementation. Introduce a vIOMMU provider that associates implementation operations, their module owner and private data. Allow one external provider to register for each non-default vIOMMU type. Look up an external provider first when allocating a vIOMMU. If none matches the requested type, represent the physical IOMMU driver as a fallback provider and dispatch through its get_viommu_size() and viommu_init() operations. A registered provider starts with one registration reference. Registration does not pin the provider module. Each successful lookup takes both a provider reference and a module reference. Unregistration removes the provider from lookup before dropping the registration reference and does not wait for existing vIOMMUs. A fallback provider instance is created for each vIOMMU allocation. Its initial users count of one represents the reference returned to that allocation. It has no registration reference and is not shared with other allocations, but otherwise follows the same lifetime rules as a registered provider. Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/iommu/iommufd/Makefile | 3 +- drivers/iommu/iommufd/iommufd_private.h | 22 ++++ drivers/iommu/iommufd/viommu.c | 29 +++-- drivers/iommu/iommufd/viommu_provider.c | 160 ++++++++++++++++++++++++ include/linux/iommufd.h | 32 +++++ 5 files changed, 234 insertions(+), 12 deletions(-) create mode 100644 drivers/iommu/iommufd/viommu_provider.c diff --git a/drivers/iommu/iommufd/Makefile b/drivers/iommu/iommufd/Makefile index 71d692c9a8f4..7ed46c286c42 100644 --- a/drivers/iommu/iommufd/Makefile +++ b/drivers/iommu/iommufd/Makefile @@ -8,7 +8,8 @@ iommufd-y :=3D \ main.o \ pages.o \ vfio_compat.o \ - viommu.o + viommu.o \ + viommu_provider.o =20 iommufd-$(CONFIG_IOMMUFD_TEST) +=3D selftest.o =20 diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommuf= d/iommufd_private.h index 6cf76f7ca379..eae607eb5d76 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -21,6 +21,28 @@ struct iommu_option; struct iommufd_device; struct dma_buf_attachment; =20 +/** + * struct iommufd_viommu_provider - vIOMMU implementation provider + * @node: entry in the provider registry + * @users: references retaining @ops, @owner, and @data; registered provid= ers + * have one registry reference plus one per live vIOMMU, while a fallback + * provider is private to one vIOMMU + * @ops: provider operations + * @owner: module implementing @ops + * @data: provider-private data retained until the final reference is rele= ased + */ +struct iommufd_viommu_provider { + struct list_head node; + const struct iommufd_viommu_provider_ops *ops; + refcount_t users; + struct module *owner; + void *data; +}; + +void iommufd_put_viommu_provider(struct iommufd_viommu_provider *provider); +struct iommufd_viommu_provider * +iommufd_get_viommu_provider(struct device *dev, enum iommu_viommu_type typ= e); + struct iommufd_sw_msi_map { struct list_head sw_msi_item; phys_addr_t sw_msi_start; diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index bf5d58d55939..66bbd6e4571d 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -11,6 +11,7 @@ void iommufd_viommu_destroy(struct iommufd_object *obj) =20 if (viommu->ops && viommu->ops->destroy) viommu->ops->destroy(viommu); + iommufd_put_viommu_provider(viommu->provider); refcount_dec(&viommu->hwpt->common.obj.users); if (viommu->kvm_file) fput(viommu->kvm_file); @@ -28,7 +29,7 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) struct iommufd_hwpt_paging *hwpt_paging; struct iommufd_viommu *viommu; struct iommufd_device *idev; - const struct iommu_ops *ops; + struct iommufd_viommu_provider *provider =3D NULL; size_t viommu_size; int rc; =20 @@ -39,16 +40,16 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucm= d) if (IS_ERR(idev)) return PTR_ERR(idev); =20 - ops =3D dev_iommu_ops(idev->dev); - if (!ops->get_viommu_size || !ops->viommu_init) { - rc =3D -EOPNOTSUPP; + provider =3D iommufd_get_viommu_provider(idev->dev, cmd->type); + if (IS_ERR(provider)) { + rc =3D PTR_ERR(provider); + provider =3D NULL; goto out_put_idev; } - - viommu_size =3D ops->get_viommu_size(idev->dev, cmd->type); + viommu_size =3D provider->ops->get_size(idev->dev, cmd->type); if (!viommu_size) { rc =3D -EOPNOTSUPP; - goto out_put_idev; + goto out_put_provider; } =20 /* @@ -57,13 +58,13 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucm= d) */ if (WARN_ON_ONCE(viommu_size < sizeof(*viommu))) { rc =3D -EOPNOTSUPP; - goto out_put_idev; + goto out_put_provider; } =20 hwpt_paging =3D iommufd_get_hwpt_paging(ucmd, cmd->hwpt_id); if (IS_ERR(hwpt_paging)) { rc =3D PTR_ERR(hwpt_paging); - goto out_put_idev; + goto out_put_provider; } =20 if (!hwpt_paging->nest_parent) { @@ -84,6 +85,9 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) viommu->type =3D cmd->type; viommu->ictx =3D ucmd->ictx; viommu->hwpt =3D hwpt_paging; + viommu->provider =3D provider; + viommu->provider_data =3D provider->data; + provider =3D NULL; refcount_inc(&viommu->hwpt->common.obj.users); INIT_LIST_HEAD(&viommu->veventqs); init_rwsem(&viommu->veventqs_rwsem); @@ -94,8 +98,9 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) */ viommu->iommu_dev =3D __iommu_get_iommu_dev(idev->dev); =20 - rc =3D ops->viommu_init(viommu, hwpt_paging->common.domain, - user_data.len ? &user_data : NULL); + rc =3D viommu->provider->ops->init(viommu, idev->dev, + hwpt_paging->common.domain, + user_data.len ? &user_data : NULL); if (rc) goto out_put_hwpt; =20 @@ -110,6 +115,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucm= d) =20 out_put_hwpt: iommufd_put_object(ucmd->ictx, &hwpt_paging->common.obj); +out_put_provider: + iommufd_put_viommu_provider(provider); out_put_idev: iommufd_put_object(ucmd->ictx, &idev->obj); return rc; diff --git a/drivers/iommu/iommufd/viommu_provider.c b/drivers/iommu/iommuf= d/viommu_provider.c new file mode 100644 index 000000000000..5dda907d2abb --- /dev/null +++ b/drivers/iommu/iommufd/viommu_provider.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 ARM Ltd. + */ +#include +#include + +#include "iommufd_private.h" + +static LIST_HEAD(viommu_providers); +static DEFINE_MUTEX(viommu_providers_lock); + +static struct iommufd_viommu_provider * +viommu_find_provider(enum iommu_viommu_type type) +{ + struct iommufd_viommu_provider *provider; + + lockdep_assert_held(&viommu_providers_lock); + list_for_each_entry(provider, &viommu_providers, node) + if (provider->ops->type =3D=3D type) + return provider; + return NULL; +} + +static void viommu_provider_put(struct iommufd_viommu_provider *provider) +{ + if (!refcount_dec_and_test(&provider->users)) + return; + if (provider->ops->release) + provider->ops->release(provider->data); + kfree(provider); +} + +void iommufd_put_viommu_provider(struct iommufd_viommu_provider *provider) +{ + struct module *owner; + + if (!provider) + return; + + owner =3D provider->owner; + viommu_provider_put(provider); + module_put(owner); +} + +/** + * iommufd_register_viommu_provider() - Register an external vIOMMU provid= er + * @ops: Provider operations, including its module owner and exact type + * @data: Private data to retain until the final provider release + * + * Only one provider may be registered per type. ops->release() is called + * after unregister and the last vIOMMU release. Registration itself does + * not pin the module; each allocation does. + * + * Return: Registration handle or ERR_PTR() on failure. + */ +struct iommufd_viommu_provider * +iommufd_register_viommu_provider(const struct iommufd_viommu_provider_ops = *ops, + void *data) +{ + struct iommufd_viommu_provider *provider; + + if (ops->type =3D=3D IOMMU_VIOMMU_TYPE_DEFAULT || + !ops->get_size || !ops->init) + return ERR_PTR(-EINVAL); + + provider =3D kzalloc_obj(*provider); + if (!provider) + return ERR_PTR(-ENOMEM); + provider->ops =3D ops; + provider->owner =3D ops->owner; + provider->data =3D data; + refcount_set(&provider->users, 1); + + mutex_lock(&viommu_providers_lock); + if (viommu_find_provider(ops->type)) { + mutex_unlock(&viommu_providers_lock); + kfree(provider); + return ERR_PTR(-EBUSY); + } + list_add_tail(&provider->node, &viommu_providers); + mutex_unlock(&viommu_providers_lock); + return provider; +} +EXPORT_SYMBOL_NS_GPL(iommufd_register_viommu_provider, "IOMMUFD"); + +/** + * iommufd_unregister_viommu_provider() - Stop new lookups of a provider + * @provider: Handle returned by iommufd_register_viommu_provider() + * + * Call once per registration. Existing vIOMMUs retain the operations, + * private data and module until destruction. This does not wait for them + * to be released. + */ +void iommufd_unregister_viommu_provider(struct iommufd_viommu_provider *pr= ovider) +{ + mutex_lock(&viommu_providers_lock); + list_del(&provider->node); + mutex_unlock(&viommu_providers_lock); + viommu_provider_put(provider); +} +EXPORT_SYMBOL_NS_GPL(iommufd_unregister_viommu_provider, "IOMMUFD"); + +static size_t fallback_viommu_get_size(struct device *dev, + enum iommu_viommu_type type) +{ + return dev_iommu_ops(dev)->get_viommu_size(dev, type); +} + +static int fallback_viommu_init(struct iommufd_viommu *viommu, + struct device *dev, struct iommu_domain *parent, + const struct iommu_user_data *user_data) +{ + const struct iommu_ops *ops =3D viommu->provider_data; + + return ops->viommu_init(viommu, parent, user_data); +} + +static const struct iommufd_viommu_provider_ops fallback_viommu_provider_o= ps =3D { + .get_size =3D fallback_viommu_get_size, + .init =3D fallback_viommu_init, +}; + +struct iommufd_viommu_provider * +iommufd_get_viommu_provider(struct device *dev, enum iommu_viommu_type typ= e) +{ + const struct iommu_ops *iommu_ops; + struct iommufd_viommu_provider *provider; + + mutex_lock(&viommu_providers_lock); + provider =3D viommu_find_provider(type); + if (provider) { + if (!try_module_get(provider->owner)) { + mutex_unlock(&viommu_providers_lock); + return ERR_PTR(-ENODEV); + } + refcount_inc(&provider->users); + mutex_unlock(&viommu_providers_lock); + return provider; + } + mutex_unlock(&viommu_providers_lock); + + iommu_ops =3D dev_iommu_ops(dev); + if (!iommu_ops->get_viommu_size || !iommu_ops->viommu_init) + return ERR_PTR(-EOPNOTSUPP); + if (!try_module_get(iommu_ops->owner)) + return ERR_PTR(-ENODEV); + + provider =3D kzalloc_obj(*provider); + if (!provider) { + module_put(iommu_ops->owner); + return ERR_PTR(-ENOMEM); + } + provider->ops =3D &fallback_viommu_provider_ops; + provider->owner =3D iommu_ops->owner; + provider->data =3D (void *)iommu_ops; + refcount_set(&provider->users, 1); + return provider; +} + diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h index 3267717f676d..7b906e0d6400 100644 --- a/include/linux/iommufd.h +++ b/include/linux/iommufd.h @@ -22,9 +22,39 @@ struct iommu_user_data_array; struct iommufd_access; struct iommufd_ctx; struct iommufd_device; +struct iommufd_viommu; struct iommufd_viommu_ops; +struct iommufd_viommu_provider; +struct module; struct page; =20 +/** + * struct iommufd_viommu_provider_ops - External vIOMMU implementation + * @owner: Module implementing the callbacks + * @type: Exact vIOMMU type implemented by this provider + * @get_size: Allocation size for a device, or zero if unsupported + * @init: Initialize the vIOMMU; private data is in viommu->provider_data + * @release: Optional final release of the registration's private data + * + * Callbacks run without the registry lock held. The operations must remain + * valid until @release completes. An allocation failure is authoritative: + * it must not cause allocation through the physical IOMMU driver instead. + */ +struct iommufd_viommu_provider_ops { + struct module *owner; + enum iommu_viommu_type type; + size_t (*get_size)(struct device *dev, enum iommu_viommu_type type); + int (*init)(struct iommufd_viommu *viommu, struct device *dev, + struct iommu_domain *parent, + const struct iommu_user_data *user_data); + void (*release)(void *data); +}; + +struct iommufd_viommu_provider * +iommufd_register_viommu_provider(const struct iommufd_viommu_provider_ops = *ops, + void *data); +void iommufd_unregister_viommu_provider(struct iommufd_viommu_provider *pr= ovider); + enum iommufd_object_type { IOMMUFD_OBJ_NONE, IOMMUFD_OBJ_ANY =3D IOMMUFD_OBJ_NONE, @@ -104,6 +134,8 @@ struct iommufd_viommu { struct iommu_device *iommu_dev; struct iommufd_hwpt_paging *hwpt; struct file *kvm_file; + struct iommufd_viommu_provider *provider; + void *provider_data; =20 const struct iommufd_viommu_ops *ops; =20 --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ADD4D566C40; Thu, 17 Sep 2026 14:03:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653835; cv=none; b=kk8pNBrTATKtR6a/5SJWireFma5RkHQ/KI/VKzdS9XQdfmDz+iQMwRBn0eSvCr3dmgHPVRHmbKr/1bXkGlebU+bU5zrP1uVzNq9HL4Dxrqmg7Ga0x+lp+iUPMKGuh06TUW+X43yOne9qX3L1Z1EcZGHBZcxecXQsR1o18Or4CkM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653835; c=relaxed/simple; bh=BXxz2KNoF2JTZ11EftliKv4k4kmet4BM0uec7ldj4Ig=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=e4FEvWlqZQ2Kd15MM5/P+9JikIR83Q13fV+Ox3TAo1h3AvWgcekcFnU7q2EImgHA5Guc2QD2kYFUNWvDZTUR5SNdXIVv5iOUWhgt6IvPi0iu9q8x1LqMPHg/FOw/bQrNwWqTCiaek28jizRgCwFC6VY+wbsJlZlYvGAy5gp1mLY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QWsnEz3K; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QWsnEz3K" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6A9A31F00893; Thu, 17 Sep 2026 14:03:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653833; bh=KLr2sxxUQXd91QPC5RoSQyGl1a4/RGG43Hu1q21LoG0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=QWsnEz3KghNGNhrqiF2PyCMDFathdcgRmvEfbEiVVHHm7Vn5n27A0ECMz7ChpuOdQ VwDfZfalR5wQiqfVvQDNSPPZW7DSnSn59wvZ4l8gnvMwit5X5hoKlqNcyvnKRsDWOR bc3yukkfxlxBApGvdTySWgroGM70yzpCsx82FlimYM88E3InsHSlfoeDw+4PoxfkLZ IFzA1ZhvSl1FUySVgaR5VxwG7cIrPEFxMCa2BCPJZbjcj4/IjqDqQC8jLy61t5RZSI Utr+YC105kRUipPwv0h/Evge7G5bmedFWiuSu/7MuqO83SxGBbkFwRfsO5ITr8AxhL yGCggWViRI51A== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 09/11] iommufd: Add the vdevice TSM request ioctl Date: Thu, 17 Sep 2026 19:31:57 +0530 Message-ID: <20260917140159.1163281-10-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A VMM needs to forward guest-originated TSM commands to the TSM implementation managing an assigned device. The IOMMUFD vdevice identifies that device within its vIOMMU and provides the appropriate dispatch point. Add IOMMU_VDEVICE_TSM_REQ to send an opaque request to a vdevice and optionally return a response. Define common operation and guest architecture identifiers for CCA, SEV and TDX requests. The ioctl return value reports dispatch or data transfer errors, while tsm_code carries the TSM-specific result. Use sockptr_t for the internal request and response buffers so TSM implementations are not tied to userspace pointers. Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/iommu/iommufd/Makefile | 2 + drivers/iommu/iommufd/iommufd_private.h | 8 ++ drivers/iommu/iommufd/main.c | 3 + drivers/iommu/iommufd/tsm.c | 98 +++++++++++++++++++++++++ drivers/iommu/iommufd/viommu.c | 2 + include/linux/iommufd.h | 5 ++ include/linux/tsm.h | 25 +++++++ include/uapi/linux/iommufd.h | 73 ++++++++++++++++++ 8 files changed, 216 insertions(+) create mode 100644 drivers/iommu/iommufd/tsm.c diff --git a/drivers/iommu/iommufd/Makefile b/drivers/iommu/iommufd/Makefile index 7ed46c286c42..105a54ff89f5 100644 --- a/drivers/iommu/iommufd/Makefile +++ b/drivers/iommu/iommufd/Makefile @@ -11,6 +11,8 @@ iommufd-y :=3D \ viommu.o \ viommu_provider.o =20 +iommufd-$(CONFIG_TSM) +=3D tsm.o + iommufd-$(CONFIG_IOMMUFD_TEST) +=3D selftest.o =20 obj-$(CONFIG_IOMMUFD) +=3D iommufd.o diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommuf= d/iommufd_private.h index eae607eb5d76..0c67f2e1f137 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -721,6 +721,14 @@ void iommufd_vdevice_destroy(struct iommufd_object *ob= j); 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); +#ifdef CONFIG_TSM +int iommufd_vdevice_tsm_req_ioctl(struct iommufd_ucmd *ucmd); +#else +static inline int iommufd_vdevice_tsm_req_ioctl(struct iommufd_ucmd *ucmd) +{ + return -EOPNOTSUPP; +} +#endif =20 static inline struct iommufd_vdevice * iommufd_get_vdevice(struct iommufd_ctx *ictx, u32 id) diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c index 8c6d43601afb..4a42cda0e8f8 100644 --- a/drivers/iommu/iommufd/main.c +++ b/drivers/iommu/iommufd/main.c @@ -432,6 +432,7 @@ union ucmd_buffer { struct iommu_veventq_alloc veventq; struct iommu_vfio_ioas vfio_ioas; struct iommu_viommu_alloc viommu; + struct iommu_vdevice_tsm_req tsm_req; #ifdef CONFIG_IOMMUFD_TEST struct iommu_test_cmd test; #endif @@ -493,6 +494,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[= ] =3D { __reserved), IOCTL_OP(IOMMU_VIOMMU_ALLOC, iommufd_viommu_alloc_ioctl, struct iommu_viommu_alloc, out_viommu_id), + IOCTL_OP(IOMMU_VDEVICE_TSM_REQ, iommufd_vdevice_tsm_req_ioctl, + struct iommu_vdevice_tsm_req, tsm_code), #ifdef CONFIG_IOMMUFD_TEST IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last), #endif diff --git a/drivers/iommu/iommufd/tsm.c b/drivers/iommu/iommufd/tsm.c new file mode 100644 index 000000000000..2fb51a86c5ec --- /dev/null +++ b/drivers/iommu/iommufd/tsm.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 ARM Ltd. + */ + +#include +#include "iommufd_private.h" + +static bool iommufd_vdevice_tsm_req_arch_valid(u32 tvm_arch) +{ + switch (tvm_arch) { + case IOMMU_VDEVICE_TSM_TVM_ARCH_CCA: + case IOMMU_VDEVICE_TSM_TVM_ARCH_SEV: + case IOMMU_VDEVICE_TSM_TVM_ARCH_TDX: + return true; + default: + return false; + } +} + +static bool iommufd_vdevice_tsm_req_op_valid(u32 op, u32 tvm_arch) +{ + switch (op) { + case TSM_REQ_VALIDATE_MMIO: + case TSM_REQ_SET_TDI_STATE: + return true; + case TSM_REQ_SEV_ENABLE_DMA: + case TSM_REQ_SEV_DISABLE_DMA: + return tvm_arch =3D=3D IOMMU_VDEVICE_TSM_TVM_ARCH_SEV; + case TSM_REQ_READ_OBJECT: + case TSM_REQ_REGEN_OBJECT: + case TSM_REQ_OBJECT_INFO: + return true; + default: + return false; + } +} + +/** + * iommufd_vdevice_tsm_req_ioctl - Forward TSM requests + * @ucmd: user command data for IOMMU_VDEVICE_TSM_REQ + * + * Resolve @iommu_vdevice_tsm_req::vdevice_id to a vdevice and pass the + * request/response buffers to its vIOMMU provider. + * + * Return: + * -errno on error. + * positive residue if response/request bytes were left unconsumed. + * if response buffer is provided, residue indicates the number of bytes + * not used in response buffer + * if there is no response buffer, residue indicates the number of bytes + * not consumed in req buffer + * 0 otherwise. + */ +int iommufd_vdevice_tsm_req_ioctl(struct iommufd_ucmd *ucmd) +{ + int ret; + struct iommufd_vdevice *vdev; + struct iommu_vdevice_tsm_req *cmd =3D ucmd->cmd; + struct tsm_guest_req_info info =3D { + .op =3D cmd->op, + .tvm_arch =3D cmd->tvm_arch, + .req =3D { + .user =3D u64_to_user_ptr(cmd->req_uptr), + .is_kernel =3D false, + }, + .req_len =3D cmd->req_len, + .resp =3D { + .user =3D u64_to_user_ptr(cmd->resp_uptr), + .is_kernel =3D false, + }, + .resp_len =3D cmd->resp_len, + }; + + if (!iommufd_vdevice_tsm_req_arch_valid(cmd->tvm_arch)) + return -EINVAL; + + if (!iommufd_vdevice_tsm_req_op_valid(cmd->op, cmd->tvm_arch)) + return -EINVAL; + + vdev =3D iommufd_get_vdevice(ucmd->ictx, cmd->vdevice_id); + if (IS_ERR(vdev)) + return PTR_ERR(vdev); + + cmd->tsm_code =3D 0; + if (!vdev->viommu->ops || !vdev->viommu->ops->vdevice_tsm_req) + ret =3D -EOPNOTSUPP; + else + ret =3D vdev->viommu->ops->vdevice_tsm_req(vdev, &info, + &cmd->tsm_code); + + /* Always copy the tsm_code as response */ + if (iommufd_ucmd_respond(ucmd, sizeof(*cmd))) + ret =3D -EFAULT; + + iommufd_put_object(ucmd->ictx, &vdev->obj); + return ret; +} diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index 66bbd6e4571d..c462086ff5df 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -3,6 +3,8 @@ */ #include #include "iommufd_private.h" +#include +#include =20 void iommufd_viommu_destroy(struct iommufd_object *obj) { diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h index 7b906e0d6400..f16b82034be9 100644 --- a/include/linux/iommufd.h +++ b/include/linux/iommufd.h @@ -27,6 +27,7 @@ struct iommufd_viommu_ops; struct iommufd_viommu_provider; struct module; struct page; +struct tsm_guest_req_info; =20 /** * struct iommufd_viommu_provider_ops - External vIOMMU implementation @@ -199,6 +200,7 @@ struct iommufd_hw_queue { * include/uapi/linux/iommufd.h) * If driver has a deinit function to revert what vdevice_i= nit op * does, it should set it to the @vdev->destroy function po= inter + * @vdevice_tsm_req: Forward a guest TSM request to a driver-owned vDEVICE * @get_hw_queue_size: Get the size of a driver-defined HW queue structure= for a * given @viommu corresponding to @queue_type. Driver = should * return 0 if HW queue aren't supported accordingly. = It is @@ -225,6 +227,9 @@ struct iommufd_viommu_ops { struct iommu_user_data_array *array); const size_t vdevice_size; int (*vdevice_init)(struct iommufd_vdevice *vdev); + ssize_t (*vdevice_tsm_req)(struct iommufd_vdevice *vdev, + struct tsm_guest_req_info *info, + u64 *tsm_code); size_t (*get_hw_queue_size)(struct iommufd_viommu *viommu, enum iommu_hw_queue_type queue_type); /* AMD's HW will add hw_queue_init simply using @hw_queue->base_addr */ diff --git a/include/linux/tsm.h b/include/linux/tsm.h index f38d6fcf9cc9..39bad60b4815 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include =20 #define TSM_REPORT_INBLOB_MAX 64 #define TSM_REPORT_OUTBLOB_MAX SZ_16M @@ -139,4 +141,27 @@ struct tsm_dev *find_tsm_dev(int id); struct pci_ide; int tsm_ide_stream_register(struct pci_ide *ide); void tsm_ide_stream_unregister(struct pci_ide *ide); + +#ifdef CONFIG_TSM +/** + * struct tsm_guest_req_info - parameters for a guest-initiated TSM request + * @op: operation for the guest-initiated request + * @tvm_arch: guest TVM architecture + * @req: request data buffer filled by guest + * @req_len: the size of @req filled by guest + * @resp: response data buffer filled by host + * @resp_len: the size of @resp buffer filled by guest + */ +struct tsm_guest_req_info { + enum iommu_vdevice_tsm_guest_req_op op; + enum iommu_vdevice_tsm_guest_tvm_arch tvm_arch; + sockptr_t req; + size_t req_len; + sockptr_t resp; + size_t resp_len; +}; +#else +struct tsm_guest_req_info; +#endif + #endif /* __TSM_H */ diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 0425d452d41e..43ed082a9421 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -57,6 +57,7 @@ enum { IOMMUFD_CMD_IOAS_CHANGE_PROCESS =3D 0x92, IOMMUFD_CMD_VEVENTQ_ALLOC =3D 0x93, IOMMUFD_CMD_HW_QUEUE_ALLOC =3D 0x94, + IOMMUFD_CMD_VDEVICE_TSM_REQ =3D 0x96, }; =20 /** @@ -1351,4 +1352,76 @@ struct iommu_hw_queue_alloc { __aligned_u64 length; }; #define IOMMU_HW_QUEUE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HW_QUEUE_ALLOC) + +/** + * enum iommu_vdevice_tsm_guest_tvm_arch - guest TVM architecture + * @IOMMU_VDEVICE_TSM_TVM_ARCH_CCA: Arm CCA TVM + * @IOMMU_VDEVICE_TSM_TVM_ARCH_SEV: AMD SEV TVM + * @IOMMU_VDEVICE_TSM_TVM_ARCH_TDX: Intel TDX TVM + */ +enum iommu_vdevice_tsm_guest_tvm_arch { + IOMMU_VDEVICE_TSM_TVM_ARCH_CCA =3D 1, + IOMMU_VDEVICE_TSM_TVM_ARCH_SEV, + IOMMU_VDEVICE_TSM_TVM_ARCH_TDX, +}; + +/** + * enum iommu_vdevice_tsm_guest_req_op - operation for guest TSM requests + * @TSM_REQ_VALIDATE_MMIO: Validate MMIO for the TDI + * @TSM_REQ_SET_TDI_STATE: Set TDI state + * @TSM_REQ_SEV_ENABLE_DMA: Enable SEV DMA + * @TSM_REQ_SEV_DISABLE_DMA: Disable SEV DMA + * @TSM_REQ_READ_OBJECT: Read a TSM object + * @TSM_REQ_REGEN_OBJECT: Regenerate a TSM object + * @TSM_REQ_OBJECT_INFO: Read TSM object information + */ +enum iommu_vdevice_tsm_guest_req_op { + TSM_REQ_VALIDATE_MMIO =3D 1, + TSM_REQ_SET_TDI_STATE, + TSM_REQ_SEV_ENABLE_DMA, + TSM_REQ_SEV_DISABLE_DMA, + TSM_REQ_READ_OBJECT, + TSM_REQ_REGEN_OBJECT, + TSM_REQ_OBJECT_INFO, +}; + +/** + * struct iommu_vdevice_tsm_req - ioctl(IOMMU_VDEVICE_TSM_REQ) + * @size: sizeof(struct iommu_vdevice_tsm_req) + * @vdevice_id: vDevice ID the guest request is for + * @op: One of enum iommu_vdevice_tsm_guest_req_op + * @tvm_arch: One of enum iommu_vdevice_tsm_guest_tvm_arch + * @req_len: Size in bytes of the input payload at @req_uptr + * @resp_len: Size in bytes of the output buffer at @resp_uptr + * @req_uptr: Userspace pointer to the guest-provided request payload + * @resp_uptr: Userspace pointer to the guest response buffer + * @tsm_code: TSM-specific result code returned by the TSM implementation + * + * Forward a TSM request to the TSM bound vDevice. This is intended for + * guest TSM/TDISP message transport where the host kernel only marshals + * bytes between userspace and the TSM implementation. + * + * The request operation is guest initiated. The TSM backend validates + * @tvm_arch against its bound TVM architecture assumptions. + * + * The request payload is read from @req_uptr/@req_len. If a response is + * expected, userspace provides @resp_uptr/@resp_len as writable storage f= or + * response bytes returned by the TSM path. + * + * The ioctl is only suitable for commands and results that the host kernel + * has no use, the host is only facilitating guest to TSM communication. + */ +struct iommu_vdevice_tsm_req { + __u32 size; + __u32 vdevice_id; + __u32 op; + __u32 tvm_arch; + __u32 req_len; + __u32 resp_len; + __aligned_u64 req_uptr; + __aligned_u64 resp_uptr; + __aligned_u64 tsm_code; +}; + +#define IOMMU_VDEVICE_TSM_REQ _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VDEVICE_TSM_RE= Q) #endif --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A99DF566C40; Thu, 17 Sep 2026 14:04:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653844; cv=none; b=kP0c2U6t5SaKJa73NJWeX2hDtyXHJaGtv/a246UbV4Hddn7oeyxDCwAoMDdcwnN2u1Kctpw/hAPkraY9XDHmXOfLicI8keRTSUnGiVSMgcdmC8S/UTfoNF5+6jOZKEK276UlmrALYvEmGH5uSolN297MYlm5Zgfkf0lRDlS55KY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653844; c=relaxed/simple; bh=yvUMMkSRoCMKaKn2zHf8uDHRZg8lkxjil26LEGrpDIg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RyzR6c/dHlofZgkJYGcHrUrDyuHAK4OEtZf4ZJFvEB5UYNYc5KsOqCWUAtegMDk/F46ssaw86odRTbQuFbdu8ACmOZUmHZi2JsFW21Uulhmv/4mgr4aqgigjyUQlD05GMKslf4sRHdONzYojExRC4L8tb8LTNaFXp4eiohVDZG8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ZMYecRa0; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ZMYecRa0" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EFC0B1F000FF; Thu, 17 Sep 2026 14:03:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653842; bh=67r4HCWaQOw8qMLQBBDGdE/J7UFL22QGlqlmf12e/L4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=ZMYecRa0o5bxpDoaXTzLvcEcgzo7m0466sU4Z+OYCjd+JnOlfMmDvQmImRgw+Gbha LLqDr1cH0/0JtMDzC9ydDm/7jiMUaEBkci8SsksGnZZt3sTvTXoKdKdcWwKedFSvAE 774CbFZOD2cNIR660EevUARvUzl1fTZF4q7SmYdAJGivJJEwLgsZIT56kJlDD8lf7F H6H8+ZRFDWwUul8ojUvNRvNDjLIT11QDNkaqKU76fvMM6amApAlEzRsrs4WadAr68+ LS1pl4W4BkAm0l8v9fSRvfBS6bfljjWT335OPYLcuDs5Cq1T0Wqpi0cEBUNZP6kBcx kVWMGLPQn6VVg== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 10/11] PCI/TSM: Remove the legacy guest request interface Date: Thu, 17 Sep 2026 19:31:58 +0530 Message-ID: <20260917140159.1163281-11-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Guest TSM requests can now be dispatched through the IOMMUFD vdevice operation. Remove the PCI-device-based guest request entry point, its scope enum and the corresponding PCI/TSM driver callback. Remove the sample guest request callback and tsm_request attribute along with the API. Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/pci/tsm/core.c | 60 -------------------------------------- include/linux/pci-tsm.h | 61 +-------------------------------------- samples/devsec/link_tsm.c | 52 +-------------------------------- 3 files changed, 2 insertions(+), 171 deletions(-) diff --git a/drivers/pci/tsm/core.c b/drivers/pci/tsm/core.c index 7082fdbe8c7e..c7e0d241e55d 100644 --- a/drivers/pci/tsm/core.c +++ b/drivers/pci/tsm/core.c @@ -398,66 +398,6 @@ int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm= , u32 tdi_id) } EXPORT_SYMBOL_GPL(pci_tsm_bind); =20 -/** - * 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 ope= rations - * @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 outp= ut - * @tsm_code: Optional TSM arch specific result code for the guest TSM - * - * This is a common entry point for requests triggered by userspace KVM-ex= it - * service handlers responding to TDI information or state change requests= . The - * scope parameter limits requests to TDISP state management, or limited d= ebug. - * This path is only suitable for commands and results that are the host k= ernel - * has no use, the host is only facilitating guest to TSM communication. - * - * Returns 0 on success and -error on failure and positive "residue" on su= ccess - * 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 r= esult - * 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 sco= pe, - 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 =3D 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 =3D to_pci_tsm_pf0(pdev->tsm); - ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock); - if ((rc =3D ACQUIRE_ERR(mutex_intr, &ops_lock))) - return rc; - - tdi =3D 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); diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h index 8869585230a3..15907bad47b7 100644 --- a/include/linux/pci-tsm.h +++ b/include/linux/pci-tsm.h @@ -4,14 +4,12 @@ #include #include #include -#include #include #include =20 struct pci_tsm; struct tsm_dev; struct kvm; -enum pci_tsm_req_scope; =20 /* * struct pci_tsm_ops - manage confidential links and security state @@ -37,14 +35,13 @@ struct pci_tsm_ops { * @disconnect: teardown the secure link * @bind: bind a TDI in preparation for it to be accepted by a TVM * @unbind: remove a TDI from secure operation with a TVM - * @guest_req: marshal TVM information and state change requests * * Context: @probe, @remove, @connect, and @disconnect run under * pci_tsm_rwsem held for write to sync with TSM unregistration and * mutual exclusion of @connect and @disconnect. @connect and * @disconnect additionally run under the DSM lock (struct * pci_tsm_pf0::lock) as well as @probe and @remove of the subfunctions. - * @bind, @unbind, and @guest_req run under pci_tsm_rwsem held for read + * @bind and @unbind run under pci_tsm_rwsem held for read * and the DSM lock. */ struct_group_tagged(pci_tsm_link_ops, link_ops, @@ -56,11 +53,6 @@ struct pci_tsm_ops { struct pci_tdi *(*bind)(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id); void (*unbind)(struct pci_tdi *tdi); - ssize_t (*guest_req)(struct pci_tdi *tdi, - 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); ); =20 /* @@ -265,46 +257,6 @@ static inline bool is_pci_tsm_pf0(struct pci_dev *pdev) return PCI_FUNC(pdev->devfn) =3D=3D 0; } =20 -/** - * enum pci_tsm_req_scope - Scope of guest requests to be validated by TSM - * - * Guest requests are a transport for a TVM to communicate with a TSM + DS= M for - * a given TDI. A TSM driver is responsible for maintaining the kernel sec= urity - * model and limit commands that may affect the host, or are otherwise out= side - * the typical TDISP operational model. - */ -enum pci_tsm_req_scope { - /** - * @PCI_TSM_REQ_INFO: Read-only, without side effects, request for - * typical TDISP collateral information like Device Interface Reports. - * No device secrets are permitted, and no device state is changed. - */ - PCI_TSM_REQ_INFO =3D 0, - /** - * @PCI_TSM_REQ_STATE_CHANGE: Request to change the TDISP state from - * UNLOCKED->LOCKED, LOCKED->RUN, or other architecture specific state - * changes to support those transitions for a TDI. No other (unrelated - * to TDISP) device / host state, configuration, or data change is - * permitted. - */ - PCI_TSM_REQ_STATE_CHANGE =3D 1, - /** - * @PCI_TSM_REQ_DEBUG_READ: Read-only request for debug information - * - * A method to facilitate TVM information retrieval outside of typical - * TDISP operational requirements. No device secrets are permitted. - */ - PCI_TSM_REQ_DEBUG_READ =3D 2, - /** - * @PCI_TSM_REQ_DEBUG_WRITE: Device state changes for debug purposes - * - * The request may affect the operational state of the device outside of - * the TDISP operational model. If allowed, requires CAP_SYS_RAW_IO, and - * will taint the kernel. - */ - PCI_TSM_REQ_DEBUG_WRITE =3D 3, -}; - #ifdef CONFIG_PCI_TSM int pci_tsm_register(struct tsm_dev *tsm_dev); void pci_tsm_unregister(struct tsm_dev *tsm_dev); @@ -321,9 +273,6 @@ int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm,= u32 tdi_id); void pci_tsm_unbind(struct pci_dev *pdev); void pci_tsm_tdi_constructor(struct pci_dev *pdev, struct pci_tdi *tdi, struct kvm *kvm, u32 tdi_id); -ssize_t pci_tsm_guest_req(struct pci_dev *pdev, enum pci_tsm_req_scope sco= pe, - sockptr_t req_in, size_t in_len, sockptr_t req_out, - size_t out_len, u64 *tsm_code); struct pci_tsm_devsec *to_pci_tsm_devsec(struct pci_tsm *tsm); void pci_tsm_init_evidence(struct pci_tsm_evidence *evidence, int slot, enum hash_algo digest_algo); @@ -346,14 +295,6 @@ static inline int pci_tsm_bind(struct pci_dev *pdev, s= truct kvm *kvm, u64 tdi_id static inline void pci_tsm_unbind(struct pci_dev *pdev) { } -static inline 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) -{ - return -ENXIO; -} #endif =20 /* private: */ diff --git a/samples/devsec/link_tsm.c b/samples/devsec/link_tsm.c index 21b6c3c7ea52..1d102dd9590a 100644 --- a/samples/devsec/link_tsm.c +++ b/samples/devsec/link_tsm.c @@ -260,22 +260,6 @@ static void devsec_link_tsm_unbind(struct pci_tdi *tdi) kfree(devsec_tdi); } =20 -static ssize_t devsec_link_tsm_guest_req(struct pci_tdi *tdi, - 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) -{ - if (!sockptr_is_kernel(req_in)) - return -ENXIO; - - dev_dbg(pci_tsm_host(tdi->pdev), "%s\n", pci_name(tdi->pdev)); - print_hex_dump_debug("devsec req_in ", DUMP_PREFIX_OFFSET, 16, 4, - req_in.kernel, min(in_len, 256u), true); - - return 0; -} - static struct pci_tsm_ops devsec_link_pci_ops =3D { .probe =3D devsec_link_tsm_pci_probe, .remove =3D devsec_link_tsm_pci_remove, @@ -283,7 +267,6 @@ static struct pci_tsm_ops devsec_link_pci_ops =3D { .disconnect =3D devsec_link_tsm_disconnect, .bind =3D devsec_link_tsm_bind, .unbind =3D devsec_link_tsm_unbind, - .guest_req =3D devsec_link_tsm_guest_req, }; =20 static void devsec_link_tsm_remove(void *tsm_dev) @@ -358,46 +341,13 @@ static ssize_t tsm_unbind_store(struct device *dev, } static DEVICE_ATTR_WO(tsm_unbind); =20 -static ssize_t tsm_request_store(struct device *dev, - struct device_attribute *attr, - const char *__buf, size_t count) -{ - ssize_t rc; - u64 tsm_code =3D 0; - struct device *host; - char req_out[16] =3D {0}; - size_t out_len =3D sizeof(req_out); - - struct pci_dev *pdev __free(pci_dev_put) =3D pci_find_device(__buf); - if (!pdev) - return -ENODEV; - - char *buf __free(kvfree) =3D kvmemdup(__buf, count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - host =3D pci_tsm_host(pdev); - if (!host || host !=3D &devsec_link_tsm->dev) - return -ENXIO; - - rc =3D pci_tsm_guest_req(pdev, PCI_TSM_REQ_INFO, KERNEL_SOCKPTR(buf), - count, KERNEL_SOCKPTR(req_out), out_len, - &tsm_code); - if (rc) - return rc; - - return count; -} -static DEVICE_ATTR_WO(tsm_request); - /* - * Facilitate testing of the bind and request flows in lieu of VFIO/IOMMUFD + * Facilitate testing of the bind flows in lieu of VFIO/IOMMUFD * support to exercise these paths. */ static struct attribute *devsec_link_attrs[] =3D { &dev_attr_tsm_bind.attr, &dev_attr_tsm_unbind.attr, - &dev_attr_tsm_request.attr, NULL, }; =20 --=20 2.43.0 From nobody Fri Sep 25 02:08:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7F67568FDF; Thu, 17 Sep 2026 14:04:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653854; cv=none; b=HIjJYXdBZSGYoTBGuAFjxdeHuRn5nkzBHLmzqaLjqd/30/j6WbCYPeprdhHI6cZt/NCt//Rm4NtsaVVjudSbc7Mtf78cw4ueMIgNyZTBkKyJpY7rPPBdJWZ4mwlPUF/prWjRtQqFzPvQ1afbLtQom8PK1F+2lkU1hASnGDA2FpQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789653854; c=relaxed/simple; bh=SzIgcFRhyWUlki2oQ4DmqvreVRHTJiQakp+PL/CM+fM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bd9jtalKZH9DTr8c77fhpmHLAU5YJQZM0Frz7hKN3M4/sjCZmWr+Uwjk1rwoIrZd7YP8exZYcYd9mpydVRty43+BcMh21aqPWinQsSAKfKClpCutSNdlkbH4/DQQ0mz2o0ILrE52qk59zNuTO0U+O9ZT5Rx00gBjznbr69cwYtg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ln9ObrKm; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ln9ObrKm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 258711F00899; Thu, 17 Sep 2026 14:04:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789653851; bh=4XhfSeK1JjeSA886SLLVaYcAmqC3NXGARVDHJeIpbCY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Ln9ObrKm3c8ikidVqHgwmHwp5yIWIZ83DHhNVCAKR97CbaOGMwxWOt0wITS1hOKmg GXHdyBOjXoGzayZo4vjQQPNE7+gUAThcvHE9G6KkYog5qBFr6kCat1mYNlDKk592kw dtU/vY5fBI3TftDHtgKM6LTMcDZ7r6hOzBXSaEyzjai1Qp7JJdTkDtA7GB9kbVv3tk jNUFRkKYwyB/ZV6Q/bndMQqeTHn6QvcrDOSxjavUczT3fi/Ca1+uyL+omIVQgpWXsW ZdTdoJ9jzmDIkvYuyD96aUHn9e2UktbvCqCxUqjQ2QCHQiOTy95djWfJwXUGKkzdiq H2x93I8X7pI7Q== From: "Aneesh Kumar K.V (Arm)" To: linux-coco@lists.linux.dev, iommu@lists.linux.dev, linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: "Aneesh Kumar K.V (Arm)" , Jason Gunthorpe , Alexey Kardashevskiy , Bjorn Helgaas , Joerg Roedel , Jonathan Cameron , Kevin Tian , Nicolin Chen , Samuel Ortiz , Steven Price , Suzuki K Poulose , Will Deacon , Xu Yilun , Shameer Kolothum , Paolo Bonzini Subject: [RFC PATCH v6 11/11] PCI/TSM: Add reference-counted contexts for vdevice providers Date: Thu, 17 Sep 2026 19:31:59 +0530 Message-ID: <20260917140159.1163281-12-aneesh.kumar@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260917140159.1163281-1-aneesh.kumar@kernel.org> References: <20260917140159.1163281-1-aneesh.kumar@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Replace the PCI/TSM bind/unbind interface with reference-counted contexts that retain the function, DSM device and Link TSM for a vdevice provider. Reject disconnect while a context is live. Keep the existing bound sysfs attribute and derive its status from successful provider binding, with balanced per-function accounting on context release. Update the ABI documentation and retire the obsolete sample bind/unbind operations in the same patch. Signed-off-by: Aneesh Kumar K.V (Arm) --- Documentation/ABI/testing/sysfs-bus-pci | 29 ++- drivers/pci/tsm/core.c | 256 +++++++++++++----------- include/linux/pci-tsm.h | 85 +++++--- samples/devsec/link_tsm.c | 96 --------- tools/testing/devsec/devsec.sh | 27 +-- 5 files changed, 211 insertions(+), 282 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/te= sting/sysfs-bus-pci index c2a5c4fe9373..5288ea4ed343 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -657,6 +657,9 @@ Description: to 'connect' to teardown the connection. This is a "link" TSM attribute, see Documentation/ABI/testing/sysfs-class-tsm. + The write fails with EBUSY while any vdevice depends on the + connection. Userspace must destroy those vdevices before + disconnecting the link. =20 What: /sys/bus/pci/devices/.../tsm/dsm Contact: linux-coco@lists.linux.dev @@ -676,17 +679,21 @@ Description: (RO) Return PCI device name of this devi= ce's DSM (Device =20 What: /sys/bus/pci/devices/.../tsm/bound Contact: linux-coco@lists.linux.dev -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. +Description: (RO) Return the device name of the TSM when this PCI function + has a successfully initialized TSM-backed vdevice binding, or + an empty line when no such binding exists. The binding is + established through VFIO/IOMMUFD and remains visible until + the provider releases its context during vdevice teardown. + Merely connecting the device to a TSM or acquiring a context + does not establish a binding. Bindings of other functions + managed by the same DSM do not affect this attribute. + + This reports the binding lifetime, not the current TDISP + (TEE Device Interface Security Protocol) state. A bound vdevice + may be UNLOCKED, and TDISP lock/unlock transitions do not + change this attribute. This attribute is only visible for + devices that support TDISP operation. This is a "link" TSM + attribute, see Documentation/ABI/testing/sysfs-class-tsm. =20 What: /sys/bus/pci/devices/.../authenticated Contact: linux-pci@vger.kernel.org diff --git a/drivers/pci/tsm/core.c b/drivers/pci/tsm/core.c index c7e0d241e55d..667328d1c686 100644 --- a/drivers/pci/tsm/core.c +++ b/drivers/pci/tsm/core.c @@ -67,11 +67,10 @@ static struct pci_tsm_pf0 *to_pci_tsm_pf0(struct pci_ts= m *tsm) =20 static inline bool is_devsec(struct pci_dev *pdev) { - return pdev->tsm && pdev->tsm->dsm_dev =3D=3D NULL && - pdev->tsm->tdi =3D=3D NULL; + return pdev->tsm && !pdev->tsm->dsm_dev; } =20 -/* 'struct pci_tsm_devsec' wraps 'struct pci_tsm' when ->tdi =3D=3D ->dsm = =3D=3D NULL */ +/* 'struct pci_tsm_devsec' wraps 'struct pci_tsm' when ->dsm_dev =3D=3D NU= LL */ struct pci_tsm_devsec *to_pci_tsm_devsec(struct pci_tsm *tsm) { struct pci_dev *pdev =3D tsm->pdev; @@ -315,96 +314,118 @@ static int remove_fn(struct pci_dev *pdev, void *dat= a) return 0; } =20 -/* - * Note, this helper only returns an error code and takes an argument for - * compatibility with the pci_walk_bus() callback prototype. pci_tsm_unbin= d() - * always succeeds. - */ -static int __pci_tsm_unbind(struct pci_dev *pdev, void *data) +bool pci_tsm_is_configured(struct pci_dev *pdev) { - struct pci_tdi *tdi; - struct pci_tsm_pf0 *tsm_pf0; - - lockdep_assert_held(&pci_tsm_rwsem); + guard(rwsem_read)(&pci_tsm_rwsem); =20 - if (!pdev->tsm) - return 0; + return !!pdev->tsm; +} +EXPORT_SYMBOL_GPL(pci_tsm_is_configured); =20 - tsm_pf0 =3D to_pci_tsm_pf0(pdev->tsm); - guard(mutex)(&tsm_pf0->lock); +struct pci_tsm_context { + struct pci_tsm_pf0 *pf0; + struct pci_dev *pdev; + struct pci_dev *dsm_dev; + struct tsm_dev *tsm_dev; + struct list_head bound_node; +}; =20 - tdi =3D pdev->tsm->tdi; - if (!tdi) - return 0; +struct pci_tsm_context *pci_tsm_context_get(struct pci_dev *pdev) +{ + struct pci_tsm_context *context; + struct pci_tsm_pf0 *pf0; + struct device *tsm_device; =20 - to_pci_tsm_ops(pdev->tsm)->unbind(tdi); - pdev->tsm->tdi =3D NULL; + guard(rwsem_read)(&pci_tsm_rwsem); + if (!pdev->tsm || !is_link_tsm(pdev->tsm->tsm_dev)) + return ERR_PTR(-EOPNOTSUPP); =20 - return 0; + pf0 =3D to_pci_tsm_pf0(pdev->tsm); + if (!pf0) + return ERR_PTR(-ENXIO); + + context =3D kzalloc_obj(*context); + if (!context) + return ERR_PTR(-ENOMEM); + + guard(mutex)(&pf0->lock); + pf0->context_users++; + context->pf0 =3D pf0; + context->pdev =3D pci_dev_get(pdev); + INIT_LIST_HEAD(&context->bound_node); + context->dsm_dev =3D pci_dev_get(pf0->base_tsm.pdev); + tsm_device =3D get_device(&pdev->tsm->tsm_dev->dev); + context->tsm_dev =3D container_of(tsm_device, struct tsm_dev, dev); + return context; } +EXPORT_SYMBOL_GPL(pci_tsm_context_get); =20 -void pci_tsm_unbind(struct pci_dev *pdev) +void pci_tsm_context_put(struct pci_tsm_context *context) { - guard(rwsem_read)(&pci_tsm_rwsem); - __pci_tsm_unbind(pdev, NULL); + struct pci_tsm_pf0 *pf0 =3D context->pf0; + + down_read(&pci_tsm_rwsem); + mutex_lock(&pf0->lock); + list_del(&context->bound_node); + if (!WARN_ON(!pf0->context_users)) + pf0->context_users--; + mutex_unlock(&pf0->lock); + up_read(&pci_tsm_rwsem); + + put_device(&context->tsm_dev->dev); + pci_dev_put(context->pdev); + pci_dev_put(context->dsm_dev); + kfree(context); } -EXPORT_SYMBOL_GPL(pci_tsm_unbind); +EXPORT_SYMBOL_GPL(pci_tsm_context_put); =20 /** - * pci_tsm_bind() - Bind @pdev as a TDI for @kvm - * @pdev: PCI device function to bind - * @kvm: Private memory attach context - * @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM = and DSM + * pci_tsm_context_mark_bound() - Publish a successful per-function binding + * @context: context acquired for the bound PCI function * - * Returns 0 on success, or a negative error code on failure. + * Call once after the provider has successfully initialized the vdevice. + * The binding remains visible in tsm/bound until pci_tsm_context_put(), + * independently of the device's TDISP state. Acquiring a context alone + * does not establish a binding. * - * Context: Caller is responsible for constraining the bind lifetime to the - * registered state of the device. For example, pci_tsm_bind() / - * pci_tsm_unbind() limited to the VFIO driver bound state of the device. + * Context: Caller holds the context's pci_tsm_pf0::lock. */ -int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id) +void pci_tsm_context_mark_bound(struct pci_tsm_context *context) { - struct pci_tsm_pf0 *tsm_pf0; - struct pci_tdi *tdi; - - if (!kvm) - return -EINVAL; - - guard(rwsem_read)(&pci_tsm_rwsem); - - if (!pdev->tsm) - return -EINVAL; - - if (!is_link_tsm(pdev->tsm->tsm_dev)) - return -ENXIO; - - tsm_pf0 =3D to_pci_tsm_pf0(pdev->tsm); - guard(mutex)(&tsm_pf0->lock); - - /* Resolve races to bind a TDI */ - if (pdev->tsm->tdi) { - if (pdev->tsm->tdi->kvm !=3D kvm) - return -EBUSY; - return 0; - } + lockdep_assert_held(&context->pf0->lock); + list_add_tail(&context->bound_node, &context->pf0->bound_contexts); +} +EXPORT_SYMBOL_GPL(pci_tsm_context_mark_bound); =20 - tdi =3D to_pci_tsm_ops(pdev->tsm)->bind(pdev, kvm, tdi_id); - if (IS_ERR(tdi)) - return PTR_ERR(tdi); +struct tsm_dev *pci_tsm_context_tsm_dev(struct pci_tsm_context *context) +{ + return context->tsm_dev; +} +EXPORT_SYMBOL_GPL(pci_tsm_context_tsm_dev); =20 - pdev->tsm->tdi =3D tdi; +struct pci_tsm_pf0 *pci_tsm_context_pf0(struct pci_tsm_context *context) +{ + return context->pf0; +} +EXPORT_SYMBOL_GPL(pci_tsm_context_pf0); =20 - return 0; +struct pci_dev *pci_tsm_context_dsm_dev(struct pci_tsm_context *context) +{ + return context->dsm_dev; } -EXPORT_SYMBOL_GPL(pci_tsm_bind); +EXPORT_SYMBOL_GPL(pci_tsm_context_dsm_dev); =20 -static void pci_tsm_unbind_all(struct pci_dev *pdev) +bool pci_tsm_context_match_device(struct pci_tsm_context *context, + struct pci_dev *pdev) { - pci_tsm_walk_fns_reverse(pdev, __pci_tsm_unbind, NULL); - __pci_tsm_unbind(pdev, NULL); + guard(rwsem_read)(&pci_tsm_rwsem); + + return pdev->tsm && is_link_tsm(pdev->tsm->tsm_dev) && + to_pci_tsm_pf0(pdev->tsm) =3D=3D context->pf0; } +EXPORT_SYMBOL_GPL(pci_tsm_context_match_device); =20 -static void __pci_tsm_disconnect(struct pci_dev *pdev) +static int __pci_tsm_disconnect(struct pci_dev *pdev) { struct pci_tsm_pf0 *tsm_pf0 =3D to_pci_tsm_pf0(pdev->tsm); const struct pci_tsm_ops *ops =3D to_pci_tsm_ops(pdev->tsm); @@ -412,21 +433,29 @@ static void __pci_tsm_disconnect(struct pci_dev *pdev) /* disconnect() mutually exclusive with subfunction pci_tsm_init() */ lockdep_assert_held_write(&pci_tsm_rwsem); =20 - pci_tsm_unbind_all(pdev); - /* - * disconnect() is uninterruptible as it may be called for device - * teardown + * A vdevice holds a context for its lifetime. Refuse to tear down the + * link until userspace destroys all dependent vdevices. + * + * disconnect() is uninterruptible as it may also be called for device + * teardown. */ - guard(mutex)(&tsm_pf0->lock); + scoped_guard(mutex, &tsm_pf0->lock) + if (tsm_pf0->context_users) + return -EBUSY; pci_tsm_walk_fns_reverse(pdev, remove_fn, NULL); ops->disconnect(pdev); + return 0; } =20 -static void pci_tsm_disconnect(struct pci_dev *pdev) +static int pci_tsm_disconnect(struct pci_dev *pdev) { - __pci_tsm_disconnect(pdev); + int ret =3D __pci_tsm_disconnect(pdev); + + if (ret) + return ret; tsm_remove(pdev->tsm); + return 0; } =20 static ssize_t disconnect_store(struct device *dev, @@ -448,35 +477,43 @@ static ssize_t disconnect_store(struct device *dev, if (!sysfs_streq(buf, dev_name(&tsm_dev->dev))) return -EINVAL; =20 - pci_tsm_disconnect(pdev); + rc =3D pci_tsm_disconnect(pdev); + if (rc) + return rc; return len; } static DEVICE_ATTR_WO(disconnect); =20 -static ssize_t bound_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t bound_show(struct device *dev, struct device_attribute *att= r, + char *buf) { struct pci_dev *pdev =3D to_pci_dev(dev); - struct pci_tsm_pf0 *tsm_pf0; - struct pci_tsm *tsm; + struct pci_tsm_context *context; + struct pci_tsm_pf0 *pf0; int rc; =20 ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem); - if ((rc =3D ACQUIRE_ERR(rwsem_read_intr, &lock))) + rc =3D ACQUIRE_ERR(rwsem_read_intr, &lock); + if (rc) return rc; =20 - tsm =3D pdev->tsm; - if (!tsm) + if (!pdev->tsm || !is_link_tsm(pdev->tsm->tsm_dev)) return sysfs_emit(buf, "\n"); - tsm_pf0 =3D to_pci_tsm_pf0(tsm); + pf0 =3D to_pci_tsm_pf0(pdev->tsm); + if (!pf0) + return -ENXIO; =20 - ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock); - if ((rc =3D ACQUIRE_ERR(mutex_intr, &ops_lock))) + ACQUIRE(mutex_intr, ops_lock)(&pf0->lock); + rc =3D ACQUIRE_ERR(mutex_intr, &ops_lock); + if (rc) return rc; =20 - if (!tsm->tdi) - return sysfs_emit(buf, "\n"); - return sysfs_emit(buf, "%s\n", dev_name(&tsm->tsm_dev->dev)); + list_for_each_entry(context, &pf0->bound_contexts, bound_node) + if (context->pdev =3D=3D pdev) + return sysfs_emit(buf, "%s\n", + dev_name(&context->tsm_dev->dev)); + + return sysfs_emit(buf, "\n"); } static DEVICE_ATTR_RO(bound); =20 @@ -955,7 +992,8 @@ static umode_t pci_tsm_attr_visible(struct kobject *kob= j, if (attr =3D=3D &dev_attr_bound.attr) { if (is_pci_tsm_pf0(pdev) && has_tee(pdev)) return attr->mode; - if (pdev->tsm && has_tee(pdev->tsm->dsm_dev)) + if (pdev->tsm && pdev->tsm->dsm_dev && + has_tee(pdev->tsm->dsm_dev)) return attr->mode; } =20 @@ -1086,22 +1124,6 @@ static struct pci_dev *find_dsm_dev(struct pci_dev *= pdev) return NULL; } =20 -/** - * pci_tsm_tdi_constructor() - base 'struct pci_tdi' initialization for li= nk TSMs - * @pdev: PCI device function representing the TDI - * @tdi: context to initialize - * @kvm: Private memory attach context - * @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM = and DSM - */ -void pci_tsm_tdi_constructor(struct pci_dev *pdev, struct pci_tdi *tdi, - struct kvm *kvm, u32 tdi_id) -{ - tdi->pdev =3D pdev; - tdi->kvm =3D kvm; - tdi->tdi_id =3D tdi_id; -} -EXPORT_SYMBOL_GPL(pci_tsm_tdi_constructor); - void pci_tsm_init_evidence(struct pci_tsm_evidence *evidence, int slot, enum hash_algo digest_algo) { @@ -1151,7 +1173,6 @@ int pci_tsm_devsec_constructor(struct pci_dev *pdev, = struct pci_tsm_devsec *tsm, return -EINVAL; =20 pci_tsm->dsm_dev =3D NULL; - pci_tsm->tdi =3D NULL; pci_tsm->pdev =3D pdev; pci_tsm->tsm_dev =3D tsm_dev; =20 @@ -1169,6 +1190,7 @@ int pci_tsm_pf0_constructor(struct pci_dev *pdev, str= uct pci_tsm_pf0 *tsm, struct tsm_dev *tsm_dev) { mutex_init(&tsm->lock); + INIT_LIST_HEAD(&tsm->bound_contexts); /* * Note, low-level TSM driver responsible for determining if it wants to * proceed with a device that has no DOE mailbox. TSM may have an @@ -1224,12 +1246,6 @@ int pci_tsm_register(struct tsm_dev *tsm_dev) return 0; } =20 -static void pci_tsm_fn_exit(struct pci_dev *pdev) -{ - __pci_tsm_unbind(pdev, NULL); - tsm_remove(pdev->tsm); -} - /** * __pci_tsm_destroy() - destroy the TSM context for @pdev * @pdev: device to cleanup @@ -1266,12 +1282,14 @@ static void __pci_tsm_destroy(struct pci_dev *pdev,= struct tsm_dev *tsm_dev) else if (tsm_dev !=3D tsm->tsm_dev) return; =20 - /* Disconnect DSMs, unlock assigned TDIs, or cleanup DSM subfunctions */ + /* Disconnect DSMs, unlock assigned TDIs, or clean up DSM subfunctions. */ if (is_link_tsm(tsm_dev)) { - if (is_pci_tsm_pf0(pdev)) - pci_tsm_disconnect(pdev); - else - pci_tsm_fn_exit(pdev); + if (is_pci_tsm_pf0(pdev)) { + if (pci_tsm_disconnect(pdev)) + pci_warn(pdev, "TSM connection is still in use\n"); + } else { + tsm_remove(pdev->tsm); + } } =20 if (is_devsec_tsm(tsm_dev) && has_tee(pdev)) { diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h index 15907bad47b7..e351e1490fd0 100644 --- a/include/linux/pci-tsm.h +++ b/include/linux/pci-tsm.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef __PCI_TSM_H #define __PCI_TSM_H +#include #include #include #include @@ -8,8 +9,8 @@ #include =20 struct pci_tsm; +struct pci_tsm_context; struct tsm_dev; -struct kvm; =20 /* * struct pci_tsm_ops - manage confidential links and security state @@ -33,16 +34,11 @@ struct pci_tsm_ops { * @connect: establish / validate a secure connection (e.g. IDE) * with the device * @disconnect: teardown the secure link - * @bind: bind a TDI in preparation for it to be accepted by a TVM - * @unbind: remove a TDI from secure operation with a TVM - * * Context: @probe, @remove, @connect, and @disconnect run under * pci_tsm_rwsem held for write to sync with TSM unregistration and * mutual exclusion of @connect and @disconnect. @connect and * @disconnect additionally run under the DSM lock (struct * pci_tsm_pf0::lock) as well as @probe and @remove of the subfunctions. - * @bind and @unbind run under pci_tsm_rwsem held for read - * and the DSM lock. */ struct_group_tagged(pci_tsm_link_ops, link_ops, struct pci_tsm *(*probe)(struct tsm_dev *tsm_dev, @@ -50,9 +46,6 @@ struct pci_tsm_ops { void (*remove)(struct pci_tsm *tsm); int (*connect)(struct pci_dev *pdev); void (*disconnect)(struct pci_dev *pdev); - struct pci_tdi *(*bind)(struct pci_dev *pdev, - struct kvm *kvm, u32 tdi_id); - void (*unbind)(struct pci_tdi *tdi); ); =20 /* @@ -81,18 +74,6 @@ struct pci_tsm_ops { size_t nonce_len); }; =20 -/** - * struct pci_tdi - Core TEE I/O Device Interface (TDI) context - * @pdev: host side representation of guest-side TDI - * @kvm: TEE VM context of bound TDI - * @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM = and DSM - */ -struct pci_tdi { - struct pci_dev *pdev; - struct kvm *kvm; - u32 tdi_id; -}; - /** * struct pci_tsm_evidence_object - General PCI/TSM blob descriptor * @data: pointer to the evidence data blob @@ -145,7 +126,6 @@ struct pci_tsm_evidence { * @dsm_dev: PCI Device Security Manager for link operations on @pdev * @tsm_dev: PCI TEE Security Manager device for Link Confidentiality or D= evice * Function Security operations - * @tdi: TDI context established by the @bind link operation * @evidence: cached evidence from SPDM session establishment (connect), or * TDISP bind (lock) * @@ -170,7 +150,6 @@ struct pci_tsm { struct pci_dev *pdev; struct pci_dev *dsm_dev; struct tsm_dev *tsm_dev; - struct pci_tdi *tdi; struct pci_tsm_evidence evidence; }; =20 @@ -178,11 +157,17 @@ 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, including conte= xts + * being initialized and not yet in @bound_contexts; a nonzero count blocks + * link disconnect and is independent of TSM and vIOMMU provider lifetimes + * @bound_contexts: bound per-function contexts, protected by @lock * @doe_mb: PCIe Data Object Exchange mailbox */ struct pci_tsm_pf0 { struct pci_tsm base_tsm; struct mutex lock; + unsigned int context_users; + struct list_head bound_contexts; struct pci_doe_mb *doe_mb; }; =20 @@ -269,10 +254,15 @@ int pci_tsm_devsec_constructor(struct pci_dev *pdev, = struct pci_tsm_devsec *tsm, void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *tsm); int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req, size_t req_sz, void *resp, size_t resp_sz); -int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id); -void pci_tsm_unbind(struct pci_dev *pdev); -void pci_tsm_tdi_constructor(struct pci_dev *pdev, struct pci_tdi *tdi, - struct kvm *kvm, u32 tdi_id); +bool pci_tsm_is_configured(struct pci_dev *pdev); +struct pci_tsm_context *pci_tsm_context_get(struct pci_dev *pdev); +void pci_tsm_context_put(struct pci_tsm_context *context); +void pci_tsm_context_mark_bound(struct pci_tsm_context *context); +struct tsm_dev *pci_tsm_context_tsm_dev(struct pci_tsm_context *context); +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 pci_tsm_devsec *to_pci_tsm_devsec(struct pci_tsm *tsm); void pci_tsm_init_evidence(struct pci_tsm_evidence *evidence, int slot, enum hash_algo digest_algo); @@ -288,12 +278,47 @@ static inline int pci_tsm_register(struct tsm_dev *ts= m_dev) static inline void pci_tsm_unregister(struct tsm_dev *tsm_dev) { } -static inline int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u64 = tdi_id) +static inline bool pci_tsm_is_configured(struct pci_dev *pdev) { - return -ENXIO; + return false; } -static inline void pci_tsm_unbind(struct pci_dev *pdev) + +static inline struct pci_tsm_context * +pci_tsm_context_get(struct pci_dev *pdev) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void pci_tsm_context_put(struct pci_tsm_context *context) +{ +} + +static inline void pci_tsm_context_mark_bound(struct pci_tsm_context *cont= ext) +{ +} + +static inline struct tsm_dev * +pci_tsm_context_tsm_dev(struct pci_tsm_context *context) +{ + return NULL; +} + +static inline struct pci_tsm_pf0 * +pci_tsm_context_pf0(struct pci_tsm_context *context) +{ + return NULL; +} + +static inline struct pci_dev * +pci_tsm_context_dsm_dev(struct pci_tsm_context *context) +{ + return NULL; +} + +static inline bool +pci_tsm_context_match_device(struct pci_tsm_context *context, struct pci_d= ev *pdev) { + return false; } #endif =20 diff --git a/samples/devsec/link_tsm.c b/samples/devsec/link_tsm.c index 1d102dd9590a..7fee075d7ab3 100644 --- a/samples/devsec/link_tsm.c +++ b/samples/devsec/link_tsm.c @@ -20,10 +20,6 @@ struct devsec_tsm_fn { struct pci_tsm pci; }; =20 -struct devsec_tsm_tdi { - struct pci_tdi pci; -}; - static struct devsec_tsm_pf0 *to_devsec_tsm_pf0(struct pci_tsm *tsm) { return container_of(tsm, struct devsec_tsm_pf0, pci.base_tsm); @@ -234,39 +230,11 @@ static void devsec_link_tsm_disconnect(struct pci_dev= *pdev) clear_bit(i, devsec_stream_ids); } =20 -static struct pci_tdi *devsec_link_tsm_bind(struct pci_dev *pdev, - struct kvm *kvm, u32 tdi_id) -{ - struct devsec_tsm_tdi *devsec_tdi =3D - kzalloc(sizeof(struct devsec_tsm_tdi), GFP_KERNEL); - - dev_dbg(pci_tsm_host(pdev), "%s\n", pci_name(pdev)); - - if (!devsec_tdi) - return ERR_PTR(-ENOMEM); - - pci_tsm_tdi_constructor(pdev, &devsec_tdi->pci, kvm, tdi_id); - - return &devsec_tdi->pci; -} - -static void devsec_link_tsm_unbind(struct pci_tdi *tdi) -{ - struct devsec_tsm_tdi *devsec_tdi =3D - container_of(tdi, struct devsec_tsm_tdi, pci); - - dev_dbg(pci_tsm_host(tdi->pdev), "%s\n", pci_name(tdi->pdev)); - - kfree(devsec_tdi); -} - static struct pci_tsm_ops devsec_link_pci_ops =3D { .probe =3D devsec_link_tsm_pci_probe, .remove =3D devsec_link_tsm_pci_remove, .connect =3D devsec_link_tsm_connect, .disconnect =3D devsec_link_tsm_disconnect, - .bind =3D devsec_link_tsm_bind, - .unbind =3D devsec_link_tsm_unbind, }; =20 static void devsec_link_tsm_remove(void *tsm_dev) @@ -292,71 +260,7 @@ static const struct faux_device_ops devsec_link_device= _ops =3D { .probe =3D devsec_link_tsm_probe, }; =20 -static struct pci_dev *pci_find_device(const char *name) -{ - struct device *dev =3D bus_find_device_by_name(&pci_bus_type, NULL, name); - - if (dev) - return to_pci_dev(dev); - return NULL; -} - -static ssize_t tsm_bind_store(struct device *dev, struct device_attribute = *attr, - const char *buf, size_t count) -{ - struct device *host; - int rc; - - struct pci_dev *pdev __free(pci_dev_put) =3D pci_find_device(buf); - if (!pdev) - return -ENODEV; - - host =3D pci_tsm_host(pdev); - if (!host || host !=3D &devsec_link_tsm->dev) - return -ENXIO; - - rc =3D pci_tsm_bind(pdev, (struct kvm *)1, pci_dev_id(pdev)); - if (rc) - return rc; - return count; -} -static DEVICE_ATTR_WO(tsm_bind); - -static ssize_t tsm_unbind_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct device *host; - - struct pci_dev *pdev __free(pci_dev_put) =3D pci_find_device(buf); - if (!pdev) - return -ENODEV; - - host =3D pci_tsm_host(pdev); - if (!host || host !=3D &devsec_link_tsm->dev) - return -ENXIO; - - pci_tsm_unbind(pdev); - return count; -} -static DEVICE_ATTR_WO(tsm_unbind); - -/* - * Facilitate testing of the bind flows in lieu of VFIO/IOMMUFD - * support to exercise these paths. - */ -static struct attribute *devsec_link_attrs[] =3D { - &dev_attr_tsm_bind.attr, - &dev_attr_tsm_unbind.attr, - NULL, -}; - -static const struct attribute_group devsec_link_group =3D { - .attrs =3D devsec_link_attrs, -}; - static const struct attribute_group *devsec_link_groups[] =3D { - &devsec_link_group, &devsec_evidence_group, NULL, }; diff --git a/tools/testing/devsec/devsec.sh b/tools/testing/devsec/devsec.sh index 6a9313e7104f..159b1a6fb133 100755 --- a/tools/testing/devsec/devsec.sh +++ b/tools/testing/devsec/devsec.sh @@ -94,14 +94,10 @@ validate_disconnected() { fn_dev=3D${FN_DEVS[$1]} host_bridge=3D$(dirname $(dirname $(readlink -f $pci_dev))) =20 - # validate that the dsm is not yet detected and that the sub-function - # is aware of any TSM capabilities + # validate that the dsm is not yet detected dsm=3D$(cat $pci_dev/tsm/dsm) || err "$LINENO from $2" - bound=3D$(cat $pci_dev/tsm/bound) || err "$LINENO from $2" [[ -z $dsm ]] || err "$LINENO from $2" - [[ -z $bound ]] || err "$LINENO from $2" [[ ! -e $fn_dev/tsm/dsm ]] || err "$LINENO from $2" - [[ ! -e $fn_dev/tsm/bound ]] || err "$LINENO from $2" [[ ! -e $fn_dev/tsm/connect ]] || err "$LINENO from $2" [[ ! -e $fn_dev/tsm/disconnect ]] || err "$LINENO from $2" } @@ -197,27 +193,6 @@ ide_test() { =20 check_evidence $pci_dev =20 - # bind both functions and validate that they display bound to - # the TSM device - echo $(basename $pci_dev) > $tsm_link/device/tsm_bind - bound=3D$(cat $pci_dev/tsm/bound) - [[ $bound =3D=3D $(basename $tsm_link) ]] || err "$LINENO" - echo $(basename $fn_dev) > $tsm_link/device/tsm_bind - bound=3D$(cat $fn_dev/tsm/bound) - [[ $bound =3D=3D $(basename $tsm_link) ]] || err "$LINENO" - - # test manual unbind - echo $(basename $pci_dev) > $tsm_link/device/tsm_unbind - bound=3D$(cat $pci_dev/tsm/bound) - [[ -z $bound ]] || err "$LINENO" - echo $(basename $fn_dev) > $tsm_link/device/tsm_unbind - bound=3D$(cat $fn_dev/tsm/bound) - [[ -z $bound ]] || err "$LINENO" - - # rebind to test automatic unbind at disconnect - echo $(basename $pci_dev) > $tsm_link/device/tsm_bind - echo $(basename $fn_dev) > $tsm_link/device/tsm_bind - # check that the links disappear at disconnect and the stream # pool is refilled echo $(basename $tsm_link) > $pci_dev/tsm/disconnect --=20 2.43.0