From: Si-Wei Liu <si-wei.liu@oracle.com> stable inclusion from stable-v6.6.158 commit b46594205234a8cfff47dc9d1196698cc342d77d category: feature bugzilla: https://atomgit.com/src-openeuler/kernel/issues/19814 CVE: CVE-2026-97994 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit a26f2e4e68ee3130e5d5acb4f58807041aaea905 ] Some device specific IOMMU parent drivers have long standing bogus behaviour that mistakenly clean up the maps during .reset. By definition, this is violation to the on-chip IOMMU ops (i.e. .set_map, or .dma_map & .dma_unmap) in those offending drivers, as the removal of internal maps is completely agnostic to the upper layer, causing inconsistent view between the userspace and the kernel. Some userspace app like QEMU gets around of this brokenness by proactively removing and adding back all the maps around vdpa device reset, but such workaround actually penaltize other well-behaved driver setup, where vdpa reset always comes with the associated mapping cost, especially for kernel vDPA devices (use_va=false) that have high cost on pinning. It's imperative to rectify this behaviour and remove the problematic code from all those non-compliant parent drivers. However, we cannot unconditionally remove the bogus map-cleaning code from the buggy .reset implementation, as there might exist userspace apps that already rely on the behaviour on some setup. Introduce a .compat_reset driver op to keep compatibility with older userspace. New and well behaved parent driver should not bother to implement such op, but only those drivers that are doing or used to do non-compliant map-cleaning reset will have to. Signed-off-by: Si-Wei Liu <si-wei.liu@oracle.com> Message-Id: <1697880319-4937-5-git-send-email-si-wei.liu@oracle.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Tested-by: Lei Yang <leiyang@redhat.com> Stable-dep-of: ccb1dc7c527f ("vhost/vdpa: reject VRING_NUM larger than device max") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Jiacheng Yu <yujiacheng3@huawei.com> Conflicts: include/linux/vdpa.h [1. Context conflicts: this tree carries a private reset-state extension (enum vdpa_reset_state, reset op taking @state), so the reset op keeps its @state argument next to the newly added compat_reset op.] Signed-off-by: Jiacheng Yu <yujiacheng3@huawei.com> --- include/linux/vdpa.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h index 09482250192c..32528aae815a 100644 --- a/include/linux/vdpa.h +++ b/include/linux/vdpa.h @@ -258,6 +258,17 @@ enum vdpa_reset_state { * VDPA_DEV_RESET_OPEN for vhost-vdpa device open * VDPA_DEV_RESET_CLOSE for vhost-vdpa device close * Returns integer: success (0) or error (< 0) + * @compat_reset: Reset device with compatibility quirks to + * accommodate older userspace. Only needed by + * parent driver which used to have bogus reset + * behaviour, and has to maintain such behaviour + * for compatibility with older userspace. + * Historically compliant driver only has to + * implement .reset, Historically non-compliant + * driver should implement both. + * @vdev: vdpa device + * @flags: compatibility quirks for reset + * Returns integer: success (0) or error (< 0) * @suspend: Suspend the device (optional) * @vdev: vdpa device * Returns integer: success (0) or error (< 0) @@ -424,6 +435,8 @@ struct vdpa_config_ops { u8 (*get_status)(struct vdpa_device *vdev); void (*set_status)(struct vdpa_device *vdev, u8 status); int (*reset)(struct vdpa_device *vdev, int state); + int (*compat_reset)(struct vdpa_device *vdev, u32 flags); +#define VDPA_RESET_F_CLEAN_MAP 1 int (*suspend)(struct vdpa_device *vdev); int (*resume)(struct vdpa_device *vdev); size_t (*get_config_size)(struct vdpa_device *vdev); -- 2.34.1