* [RFC PATCH v9 0/4] virtio-gpu: Add userptr support for compute workloads
@ 2026-09-24 9:55 Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 1/4] drm/virtio-gpu: Add VIRTIO_GPU_CAPSET_ROCM capability Honglei Huang
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Honglei Huang @ 2026-09-24 9:55 UTC (permalink / raw)
To: dmitry.osipenko, odaki, airlied, kraxel
Cc: gurchetansingh, olvaffe, Ray.Huang, dri-devel, virtualization,
linux-kernel, Honglei Huang
Hello,
This series adds virtio-gpu userptr support for ROCm native compute
contexts. The guest kernel pins an existing userspace mapping with
FOLL_LONGTERM and exposes it to the host as ordinary CREATE_BLOB
backing entries, avoiding a second shmem allocation and memcpy.
A non-zero userptr field selects that path. Probe
VIRTGPU_PARAM_USERPTR before using the field so an older guest kernel
rejects the request instead of silently creating a shmem blob.
GPU-readonly backing is requested with the virtio CREATE_BLOB wire
flag VIRTIO_GPU_BLOB_FLAG_USE_READONLY (also
VIRTGPU_BLOB_FLAG_USE_READONLY). The device MUST NOT write such a
resource. The guest accepts that flag and pins without FOLL_WRITE
only when the device advertised VIRTIO_GPU_F_BLOB_READONLY
(VIRTGPU_PARAM_BLOB_READONLY). Otherwise CREATE_BLOB fails, matching
the CROSS_DEVICE host-capability check.
Patches overview:
1. Add VIRTIO_GPU_CAPSET_ROCM capability for compute workloads
2. Extend DRM/virtio UAPI with userptr, PARAM_USERPTR,
USE_READONLY, and F_BLOB_READONLY
3. Implement core userptr functionality with page management
4. Wire blob ioctl creation to userptr objects and probe the
readonly feature
V9 changes:
- Marked series RFC pending a virtio-gpu spec update for
F_BLOB_READONLY / BLOB_FLAG_USE_READONLY (Odaki)
- Moved VIRTIO_GPU_CAPSET_ROCM/VIRTGPU_DRM_CAPSET_ROCM from ID 8 to
10 to avoid the gfxstream/rutabaga_gfx 7-9 collision (Odaki)
- Added the missing VIRTGPU_DRM_CAPSET_ROCM alias (Odaki, sashiko-bot)
- Dropped the kernel/guest-internal wire-flag comment from the UAPI
header and reworded "Guest VA" to "address" in the userptr field
comment (Odaki)
- Fixed a BUG() on every virtio-gpu probe: VIRTIO_GPU_F_BLOB_READONLY
was queried with virtio_has_feature() but never added to the
driver's features[] array (Odaki)
- Removed the dead TRANSFER_TO_HOST userptr DMA-sync branches added
to virtgpu_vq.c; verify_blob() already rejects VIRTGPU_BLOB_MEM_GUEST
for that ioctl, so the branches were unreachable (Odaki)
- Documented the DMA mapping and bounce buffer behavior of
virtio_gpu_userptr_restore() and dropped the zero-copy claim from
the commit title and message (Odaki)
V8 changes:
- Drop guest-only USERPTR ioctl flags; a non-zero userptr field
selects the path
- Add VIRTGPU_PARAM_USERPTR so older kernels do not silently
ignore the new field
- Request GPU-readonly backing with virtio wire flag USE_READONLY
- Accept USE_READONLY and omit FOLL_WRITE only if the host
advertised VIRTIO_GPU_F_BLOB_READONLY
- Restore userptr blobs after hibernation without the shmem path
V7 changes:
- Mask guest-only DRM flags out of CREATE_BLOB wire blob_flags
- Clear userptr->pages after pin failure to avoid double-free
- DMA-map userptr SG only when virtio_gpu_use_dma_api() is required
- Use DMA_TO_DEVICE for readonly blobs
- Sync userptr SG for the device on TRANSFER_TO_HOST
- Mark writable pages dirty when unpinning
- Reject USERPTR unless blob_mem is VIRTGPU_BLOB_MEM_GUEST
- Disallow PRIME export of userptr objects
- Note that CAPSET_ROCM uses ID 8 because ID 7 is taken by VIRCL
V6 changes:
- Rebase onto drm-misc-next
- Keep USE_USERPTR / USERPTR_RDONLY as guest-only DRM flags; drop the
virtio wire-header patch (5 patches down to 4)
- Fix userptr lifetime, DMA mapping, memlock accounting, alignment
checks, and PRIME SG export
- Updated corresponding cover letter and commit messages
V5 changes:
- Add VIRTIO_GPU_BLOB_FLAG_USERPTR_RDONLY definition to patch 2
- Dropped unused VIRTIO_GPU_F_RESOURCE_USERPTR feature bit in patch 2
- Included VIRTIO_GPU_BLOB_FLAG_USERPTR_RDONLY in VIRTGPU_BLOB_FLAG_USE_MASK in patch 5
- Add check for userptr feature in patch 5 before creating userptr blob resource
- Updated corresponding cover letter and commit messages
V4 changes:
- Renamed VIRTIO_GPU_CAPSET_HSAKMT to VIRTIO_GPU_CAPSET_ROCM
- Remove userptr feature probing cause it can reuse the guest
blob resource code path, reduce patch count from 6 to 5
- Updated corresponding commit messages
- Consolidated userptr feature detection in final patch
- Update corresponding cover letter content
V3 changes:
- Split into focused patches for easier review
- Removed complex interval tree userptr management
- Simplified resource creation without deduplication
- Added VIRTGPU_PARAM_RESOURCE_USERPTR for feature detection
- Improved UAPI documentation and error handling
- Enhanced code quality with proper cleanup paths
- Removed MMU notifier dependencies for simplicity
- Fixed resource lifecycle management issues
V2: - Split add HSAKMT context and blob userptr resource to two patches.
- Remove MMU notifier related patches, cause use not moveable user space
memory with MMU notifier is not a good idea.
- Remove HSAKMT context check when create context, let all the context
support the userptr feature.
- Remove MMU notifier related content in cover letter.
- Add more comments for patch 6 in cover letter.
Previous version:
https://lore.kernel.org/lkml/20260918095940.2253018-2-honghuan@amd.com/
Honglei Huang (4):
drm/virtio-gpu: Add VIRTIO_GPU_CAPSET_ROCM capability
drm/virtgpu api: add blob userptr resource
drm/virtio: implement userptr resource support
drm/virtio: wire blob ioctl creation to userptr objects
drivers/gpu/drm/virtio/Makefile | 3 +-
drivers/gpu/drm/virtio/virtgpu_debugfs.c | 1 +
drivers/gpu/drm/virtio/virtgpu_drv.c | 1 +
drivers/gpu/drm/virtio/virtgpu_drv.h | 41 ++++
drivers/gpu/drm/virtio/virtgpu_ioctl.c | 35 +++-
drivers/gpu/drm/virtio/virtgpu_kms.c | 8 +-
drivers/gpu/drm/virtio/virtgpu_object.c | 22 ++
drivers/gpu/drm/virtio/virtgpu_userptr.c | 348 +++++++++++++++++++++++++++++++
include/uapi/drm/virtgpu_drm.h | 11 +
include/uapi/linux/virtio_gpu.h | 6 +
10 files changed, 468 insertions(+), 8 deletions(-)
create mode 100644 drivers/gpu/drm/virtio/virtgpu_userptr.c
base-commit: 766bfba0f3bae329f99b42dcabc3ef11fa368f0b
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC PATCH v9 1/4] drm/virtio-gpu: Add VIRTIO_GPU_CAPSET_ROCM capability
2026-09-24 9:55 [RFC PATCH v9 0/4] virtio-gpu: Add userptr support for compute workloads Honglei Huang
@ 2026-09-24 9:55 ` Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 2/4] drm/virtgpu api: add blob userptr resource Honglei Huang
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Honglei Huang @ 2026-09-24 9:55 UTC (permalink / raw)
To: dmitry.osipenko, odaki, airlied, kraxel
Cc: gurchetansingh, olvaffe, Ray.Huang, dri-devel, virtualization,
linux-kernel, Honglei Huang
Add a new GPU capability set VIRTIO_GPU_CAPSET_ROCM to support
ROCm compute workloads in virtualized environments.
ROCm (Radeon Open Compute) is AMD's open-source software platform
for GPU compute and HPC workloads.
ID 7 is already used by VIRCL, and gfxstream's rutabaga_gfx has
already claimed IDs 7-9 ahead of any spec or kernel header update,
so ROCm uses ID 10 to avoid a further collision. A matching
virtio-gpu specification change is in progress to register this
assignment.
Also add the matching VIRTGPU_DRM_CAPSET_ROCM alias in virtgpu_drm.h
for consistency with the other capability sets exposed through
DRM_IOCTL_VIRTGPU_GET_CAPS.
Signed-off-by: Honglei Huang <honghuan@amd.com>
---
include/uapi/drm/virtgpu_drm.h | 1 +
include/uapi/linux/virtio_gpu.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
index 95587e12ae..9b886e9772 100644
--- a/include/uapi/drm/virtgpu_drm.h
+++ b/include/uapi/drm/virtgpu_drm.h
@@ -170,6 +170,7 @@ struct drm_virtgpu_3d_wait {
#define VIRTGPU_DRM_CAPSET_VENUS 4
#define VIRTGPU_DRM_CAPSET_CROSS_DOMAIN 5
#define VIRTGPU_DRM_CAPSET_DRM 6
+#define VIRTGPU_DRM_CAPSET_ROCM 10
struct drm_virtgpu_get_caps {
__u32 cap_set_id;
__u32 cap_set_ver;
diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
index 4f530d9005..e22049835e 100644
--- a/include/uapi/linux/virtio_gpu.h
+++ b/include/uapi/linux/virtio_gpu.h
@@ -321,6 +321,7 @@ struct virtio_gpu_cmd_submit {
#define VIRTIO_GPU_CAPSET_VENUS 4
#define VIRTIO_GPU_CAPSET_CROSS_DOMAIN 5
#define VIRTIO_GPU_CAPSET_DRM 6
+#define VIRTIO_GPU_CAPSET_ROCM 10
/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
struct virtio_gpu_get_capset_info {
base-commit: 766bfba0f3bae329f99b42dcabc3ef11fa368f0b
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC PATCH v9 2/4] drm/virtgpu api: add blob userptr resource
2026-09-24 9:55 [RFC PATCH v9 0/4] virtio-gpu: Add userptr support for compute workloads Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 1/4] drm/virtio-gpu: Add VIRTIO_GPU_CAPSET_ROCM capability Honglei Huang
@ 2026-09-24 9:55 ` Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 3/4] drm/virtio: implement userptr resource support Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 4/4] drm/virtio: wire blob ioctl creation to userptr objects Honglei Huang
3 siblings, 0 replies; 5+ messages in thread
From: Honglei Huang @ 2026-09-24 9:55 UTC (permalink / raw)
To: dmitry.osipenko, odaki, airlied, kraxel
Cc: gurchetansingh, olvaffe, Ray.Huang, dri-devel, virtualization,
linux-kernel, Honglei Huang
Add a userptr address field so userspace can request a blob backed
by an existing process mapping. A non-zero userptr selects that
path; probe VIRTGPU_PARAM_USERPTR before using the field.
GPU-readonly backing is requested with the virtio CREATE_BLOB wire
flag VIRTIO_GPU_BLOB_FLAG_USE_READONLY (also
VIRTGPU_BLOB_FLAG_USE_READONLY). The device MUST NOT write such a
resource. The guest pins without FOLL_WRITE only when the device
advertised VIRTIO_GPU_F_BLOB_READONLY (VIRTGPU_PARAM_BLOB_READONLY).
Signed-off-by: Honglei Huang <honghuan@amd.com>
---
include/uapi/drm/virtgpu_drm.h | 10 ++++++++++
include/uapi/linux/virtio_gpu.h | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
index 9b886e9772..c890c754d8 100644
--- a/include/uapi/drm/virtgpu_drm.h
+++ b/include/uapi/drm/virtgpu_drm.h
@@ -99,6 +99,8 @@ struct drm_virtgpu_execbuffer {
#define VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs 7 /* Bitmask of supported capability set ids */
#define VIRTGPU_PARAM_EXPLICIT_DEBUG_NAME 8 /* Ability to set debug name from userspace */
#define VIRTGPU_PARAM_BLOB_ALIGNMENT 9 /* Device alignment requirements for blobs */
+#define VIRTGPU_PARAM_USERPTR 10 /* CREATE_BLOB userptr field is supported */
+#define VIRTGPU_PARAM_BLOB_READONLY 11 /* VIRTGPU_BLOB_FLAG_USE_READONLY is honored */
struct drm_virtgpu_getparam {
__u64 param;
@@ -187,6 +189,7 @@ struct drm_virtgpu_resource_create_blob {
#define VIRTGPU_BLOB_FLAG_USE_MAPPABLE 0x0001
#define VIRTGPU_BLOB_FLAG_USE_SHAREABLE 0x0002
#define VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004
+#define VIRTGPU_BLOB_FLAG_USE_READONLY 0x0008
/* zero is invalid blob_mem */
__u32 blob_mem;
__u32 blob_flags;
@@ -206,6 +209,13 @@ struct drm_virtgpu_resource_create_blob {
#define DRM_VIRTGPU_BLOB_FLAG_HINT_DEFER_MAPPING 0x0001
__u32 blob_hints;
__u32 pad2;
+
+ /*
+ * Non-zero selects the userptr path and is valid only with
+ * VIRTGPU_BLOB_MEM_GUEST. Must be 0 otherwise. Probe
+ * VIRTGPU_PARAM_USERPTR before using this field.
+ */
+ __u64 userptr;
};
#define VIRTGPU_CONTEXT_PARAM_CAPSET_ID 0x0001
diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
index e22049835e..af95374758 100644
--- a/include/uapi/linux/virtio_gpu.h
+++ b/include/uapi/linux/virtio_gpu.h
@@ -72,6 +72,10 @@
* must be aligned to that value.
*/
#define VIRTIO_GPU_F_BLOB_ALIGNMENT 5
+/*
+ * VIRTIO_GPU_BLOB_FLAG_USE_READONLY
+ */
+#define VIRTIO_GPU_F_BLOB_READONLY 6
enum virtio_gpu_ctrl_type {
VIRTIO_GPU_UNDEFINED = 0,
@@ -415,6 +419,7 @@ struct virtio_gpu_resource_create_blob {
#define VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE 0x0001
#define VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE 0x0002
#define VIRTIO_GPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004
+#define VIRTIO_GPU_BLOB_FLAG_USE_READONLY 0x0008
/* zero is invalid blob mem */
__le32 blob_mem;
__le32 blob_flags;
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC PATCH v9 3/4] drm/virtio: implement userptr resource support
2026-09-24 9:55 [RFC PATCH v9 0/4] virtio-gpu: Add userptr support for compute workloads Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 1/4] drm/virtio-gpu: Add VIRTIO_GPU_CAPSET_ROCM capability Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 2/4] drm/virtgpu api: add blob userptr resource Honglei Huang
@ 2026-09-24 9:55 ` Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 4/4] drm/virtio: wire blob ioctl creation to userptr objects Honglei Huang
3 siblings, 0 replies; 5+ messages in thread
From: Honglei Huang @ 2026-09-24 9:55 UTC (permalink / raw)
To: dmitry.osipenko, odaki, airlied, kraxel
Cc: gurchetansingh, olvaffe, Ray.Huang, dri-devel, virtualization,
linux-kernel, Honglei Huang
Add userptr blob objects so the guest kernel can pin an existing
userspace mapping and advertise it as CREATE_BLOB backing entries.
- New virtio_gpu_object_userptr type for userptr resources
- Pin pages with pin_user_pages_fast() and FOLL_LONGTERM
- Omit FOLL_WRITE when VIRTGPU_BLOB_FLAG_USE_READONLY is set
- Charge FOLL_LONGTERM pins against RLIMIT_MEMLOCK
- DMA-map the scatterlist only when virtio_gpu_use_dma_api() is
required; use DMA_TO_DEVICE for USE_READONLY blobs
- Mark writable pages dirty when unpinning
- Keep pages pinned until RESOURCE_UNREF is queued; drop them from
cleanup_object() on the unref response or on create failure
- Clear userptr->pages on pin failure to avoid double-free on cleanup
- Reject unaligned or overflowing userptr ranges at create time
- Disallow PRIME export of userptr objects
- Save CREATE_BLOB params and restore userptr resources after
hibernation without using the shmem restore path
The hibernation restore path maps with the same attrs and direction
used at create time, as documented next to the call. Like shmem
blobs, the DMA API can still bounce through a buffer there. This
design only avoids a second guest side shmem allocation and memcpy,
nothing more.
Signed-off-by: Honglei Huang <honghuan@amd.com>
---
drivers/gpu/drm/virtio/Makefile | 3 +-
drivers/gpu/drm/virtio/virtgpu_drv.h | 41 +++
drivers/gpu/drm/virtio/virtgpu_object.c | 22 ++
drivers/gpu/drm/virtio/virtgpu_userptr.c | 348 +++++++++++++++++++++++
4 files changed, 413 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/virtio/virtgpu_userptr.c
diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
index d2e1788a82..fe7332a621 100644
--- a/drivers/gpu/drm/virtio/Makefile
+++ b/drivers/gpu/drm/virtio/Makefile
@@ -6,6 +6,7 @@
virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_gem.o virtgpu_vram.o \
virtgpu_display.o virtgpu_vq.o \
virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o \
- virtgpu_ioctl.o virtgpu_prime.o virtgpu_trace_points.o virtgpu_submit.o
+ virtgpu_ioctl.o virtgpu_prime.o virtgpu_trace_points.o virtgpu_submit.o \
+ virtgpu_userptr.o
obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 9df4c71173..b58e5afe8f 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -105,6 +105,7 @@ struct virtio_gpu_object_params {
uint32_t blob_flags;
uint64_t blob_id;
uint32_t blob_hints;
+ uint64_t userptr;
};
struct virtio_gpu_object {
@@ -138,12 +139,41 @@ struct virtio_gpu_object_vram {
struct drm_mm_node vram_node;
};
+struct virtio_gpu_object_userptr;
+
+struct virtio_gpu_object_userptr_ops {
+ int (*get_pages)(struct virtio_gpu_object_userptr *userptr);
+ void (*put_pages)(struct virtio_gpu_object_userptr *userptr);
+};
+
+struct virtio_gpu_object_userptr {
+ struct virtio_gpu_object base;
+ const struct virtio_gpu_object_userptr_ops *ops;
+ struct mutex lock;
+
+ uint64_t start;
+ uint32_t npages;
+ uint32_t bo_handle;
+ uint32_t flags;
+
+ struct virtio_gpu_device *vgdev;
+ struct drm_file *file;
+ struct page **pages;
+ struct sg_table *sgt;
+ bool dma_mapped;
+ enum dma_data_direction dma_dir;
+ struct mm_struct *mm;
+};
+
#define to_virtio_gpu_shmem(virtio_gpu_object) \
container_of((virtio_gpu_object), struct virtio_gpu_object_shmem, base)
#define to_virtio_gpu_vram(virtio_gpu_object) \
container_of((virtio_gpu_object), struct virtio_gpu_object_vram, base)
+#define to_virtio_gpu_userptr(virtio_gpu_object) \
+ container_of((virtio_gpu_object), struct virtio_gpu_object_userptr, base)
+
struct virtio_gpu_object_array {
struct ww_acquire_ctx ticket;
struct list_head next;
@@ -284,6 +314,7 @@ struct virtio_gpu_device {
bool has_host_visible;
bool has_context_init;
bool has_blob_alignment;
+ bool has_blob_readonly;
bool hibernated;
struct virtio_shm_region host_visible_region;
struct drm_mm host_visible_mm;
@@ -562,4 +593,14 @@ void virtio_gpu_vram_map_deferred(struct virtio_gpu_object_vram *vram);
int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
+/* virtgpu_userptr.c */
+int virtio_gpu_userptr_create(struct virtio_gpu_device *vgdev,
+ struct drm_file *file,
+ struct virtio_gpu_object_params *params,
+ struct virtio_gpu_object **bo_ptr);
+bool virtio_gpu_is_userptr(struct virtio_gpu_object *bo);
+int virtio_gpu_userptr_restore(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *bo,
+ struct virtio_gpu_mem_entry **ents,
+ unsigned int *nents);
#endif
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index 49899485be..ab21494b1c 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -91,6 +91,16 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo)
drm_gem_free_mmap_offset(&vram->base.base.base);
drm_gem_object_release(&vram->base.base.base);
kfree(vram);
+ } else if (virtio_gpu_is_userptr(bo)) {
+ struct virtio_gpu_object_userptr *userptr =
+ to_virtio_gpu_userptr(bo);
+
+ mutex_lock(&userptr->lock);
+ userptr->ops->put_pages(userptr);
+ mutex_unlock(&userptr->lock);
+ mutex_destroy(&userptr->lock);
+ drm_gem_object_release(&userptr->base.base.base);
+ kfree(userptr);
} else {
drm_gem_object_release(&bo->base.base);
kfree(bo);
@@ -316,6 +326,18 @@ int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev)
continue;
}
+ if (virtio_gpu_is_userptr(bo)) {
+ ret = virtio_gpu_userptr_restore(vgdev, bo, &ents,
+ &nents);
+ if (ret)
+ break;
+
+ virtio_gpu_cmd_resource_create_blob(vgdev, bo,
+ &bo->params,
+ ents, nents);
+ continue;
+ }
+
if (bo->params.blob || bo->attached) {
ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents,
&nents);
diff --git a/drivers/gpu/drm/virtio/virtgpu_userptr.c b/drivers/gpu/drm/virtio/virtgpu_userptr.c
new file mode 100644
index 0000000000..4fb367d0d6
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_userptr.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/dma-mapping.h>
+#include <linux/limits.h>
+#include <linux/mm.h>
+#include <linux/overflow.h>
+#include <linux/pid.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/signal.h>
+#include <linux/vmalloc.h>
+
+#include "virtgpu_drv.h"
+#include <drm/drm_gem.h>
+
+static void virtio_gpu_userptr_free(struct drm_gem_object *obj)
+{
+ struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
+ struct virtio_gpu_device *vgdev = obj->dev->dev_private;
+
+ /*
+ * Pages stay pinned until RESOURCE_UNREF finishes. Its callback
+ * calls virtio_gpu_cleanup_object to drop them.
+ */
+ if (bo->created) {
+ virtio_gpu_remove_from_restore_list(bo);
+ virtio_gpu_cmd_unref_resource(vgdev, bo, false);
+ virtio_gpu_notify(vgdev);
+ return;
+ }
+
+ virtio_gpu_cleanup_object(bo);
+}
+
+static struct dma_buf *
+virtio_gpu_userptr_prime_export(struct drm_gem_object *obj, int flags)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static const struct drm_gem_object_funcs virtio_gpu_userptr_funcs = {
+ .open = virtio_gpu_gem_object_open,
+ .close = virtio_gpu_gem_object_close,
+ .free = virtio_gpu_userptr_free,
+ .export = virtio_gpu_userptr_prime_export,
+};
+
+bool virtio_gpu_is_userptr(struct virtio_gpu_object *bo)
+{
+ return bo->base.base.funcs == &virtio_gpu_userptr_funcs;
+}
+
+static int
+virtio_gpu_userptr_get_pages(struct virtio_gpu_object_userptr *userptr)
+{
+ unsigned int flag = FOLL_LONGTERM;
+ unsigned int num_pages, pinned = 0;
+ int ret = 0;
+
+ if (userptr->pages)
+ return 0;
+
+ userptr->pages = kvmalloc_array(userptr->npages, sizeof(struct page *),
+ GFP_KERNEL);
+ if (!userptr->pages)
+ return -ENOMEM;
+
+ if (!(userptr->flags & VIRTGPU_BLOB_FLAG_USE_READONLY))
+ flag |= FOLL_WRITE;
+
+ do {
+ num_pages = userptr->npages - pinned;
+
+ ret = pin_user_pages_fast(userptr->start + pinned * PAGE_SIZE,
+ num_pages, flag,
+ userptr->pages + pinned);
+
+ if (ret < 0) {
+ if (pinned)
+ unpin_user_pages(userptr->pages, pinned);
+ kvfree(userptr->pages);
+ userptr->pages = NULL;
+ return ret;
+ }
+
+ pinned += ret;
+
+ } while (pinned < userptr->npages);
+
+ return 0;
+}
+
+static void
+virtio_gpu_userptr_unaccount(struct virtio_gpu_object_userptr *userptr)
+{
+ if (!userptr->mm)
+ return;
+
+ atomic64_sub(userptr->npages, &userptr->mm->pinned_vm);
+ mmdrop(userptr->mm);
+ userptr->mm = NULL;
+}
+
+static void
+virtio_gpu_userptr_put_pages(struct virtio_gpu_object_userptr *userptr)
+{
+ struct drm_device *dev = userptr->base.base.base.dev;
+
+ if (userptr->sgt) {
+ if (userptr->dma_mapped)
+ dma_unmap_sgtable(drm_dev_dma_dev(dev), userptr->sgt,
+ userptr->dma_dir, 0);
+ userptr->dma_mapped = false;
+ sg_free_table(userptr->sgt);
+ kfree(userptr->sgt);
+ userptr->sgt = NULL;
+ }
+
+ if (userptr->pages) {
+ bool dirty = !(userptr->flags & VIRTGPU_BLOB_FLAG_USE_READONLY);
+
+ unpin_user_pages_dirty_lock(userptr->pages, userptr->npages,
+ dirty);
+ kvfree(userptr->pages);
+ userptr->pages = NULL;
+ }
+
+ virtio_gpu_userptr_unaccount(userptr);
+}
+
+static int
+virtio_gpu_userptr_get_entries(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object_userptr *userptr,
+ struct virtio_gpu_mem_entry **ents,
+ unsigned int *nents)
+{
+ bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
+ struct scatterlist *sg;
+ unsigned int count;
+ int si;
+
+ count = use_dma_api ? userptr->sgt->nents : userptr->sgt->orig_nents;
+ if (!count)
+ return -EINVAL;
+
+ *ents = kvmalloc_array(count, sizeof(**ents), GFP_KERNEL);
+ if (!*ents)
+ return -ENOMEM;
+
+ if (use_dma_api) {
+ for_each_sgtable_dma_sg(userptr->sgt, sg, si) {
+ (*ents)[si].addr = cpu_to_le64(sg_dma_address(sg));
+ (*ents)[si].length = cpu_to_le32(sg_dma_len(sg));
+ (*ents)[si].padding = 0;
+ }
+ } else {
+ for_each_sgtable_sg(userptr->sgt, sg, si) {
+ (*ents)[si].addr = cpu_to_le64(sg_phys(sg));
+ (*ents)[si].length = cpu_to_le32(sg->length);
+ (*ents)[si].padding = 0;
+ }
+ }
+
+ *nents = count;
+ return 0;
+}
+
+int virtio_gpu_userptr_restore(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *bo,
+ struct virtio_gpu_mem_entry **ents,
+ unsigned int *nents)
+{
+ struct virtio_gpu_object_userptr *userptr = to_virtio_gpu_userptr(bo);
+ int ret;
+
+ mutex_lock(&userptr->lock);
+ if (!userptr->sgt || !userptr->pages) {
+ mutex_unlock(&userptr->lock);
+ return -EINVAL;
+ }
+
+ if (userptr->dma_mapped) {
+ struct device *dev = drm_dev_dma_dev(vgdev->ddev);
+
+ dma_unmap_sgtable(dev, userptr->sgt, userptr->dma_dir, 0);
+ userptr->dma_mapped = false;
+
+ ret = dma_map_sgtable(dev, userptr->sgt, userptr->dma_dir, 0);
+ if (ret) {
+ mutex_unlock(&userptr->lock);
+ return ret;
+ }
+ userptr->dma_mapped = true;
+ }
+
+ ret = virtio_gpu_userptr_get_entries(vgdev, userptr, ents, nents);
+ mutex_unlock(&userptr->lock);
+ return ret;
+}
+
+static int
+virtio_gpu_userptr_init(struct drm_device *dev, struct drm_file *file,
+ struct virtio_gpu_object_userptr *userptr,
+ struct virtio_gpu_object_params *params,
+ const struct virtio_gpu_object_userptr_ops *ops)
+{
+ struct drm_gem_object *obj;
+ int ret;
+
+ userptr->start = params->userptr;
+ userptr->npages = params->size >> PAGE_SHIFT;
+ userptr->flags = params->blob_flags;
+
+ mutex_init(&userptr->lock);
+ userptr->vgdev = dev->dev_private;
+ userptr->file = file;
+ userptr->ops = ops;
+
+ /*
+ * Get the resource id before GEM init, so a failed call here
+ * just needs a plain kfree and no special id zero check in the
+ * shared resource_id_put helper.
+ */
+ ret = virtio_gpu_resource_id_get(userptr->vgdev,
+ &userptr->base.hw_res_handle);
+ if (ret) {
+ mutex_destroy(&userptr->lock);
+ return ret;
+ }
+
+ obj = &userptr->base.base.base;
+ obj->funcs = &virtio_gpu_userptr_funcs;
+
+ drm_gem_private_object_init(dev, obj, params->size);
+ INIT_LIST_HEAD(&userptr->base.restore_node);
+
+ return 0;
+}
+
+static const struct virtio_gpu_object_userptr_ops virtio_gpu_userptr_ops = {
+ .get_pages = virtio_gpu_userptr_get_pages,
+ .put_pages = virtio_gpu_userptr_put_pages,
+};
+
+int virtio_gpu_userptr_create(struct virtio_gpu_device *vgdev,
+ struct drm_file *file,
+ struct virtio_gpu_object_params *params,
+ struct virtio_gpu_object **bo_ptr)
+{
+ struct virtio_gpu_object_userptr *userptr;
+ struct virtio_gpu_mem_entry *ents = NULL;
+ struct sg_table *sgt;
+ struct mm_struct *mm;
+ unsigned long lock_limit;
+ unsigned long start;
+ unsigned long end;
+ s64 new_pinned;
+ unsigned int nents;
+ int ret;
+
+ *bo_ptr = NULL;
+
+ if (!params->size || !IS_ALIGNED(params->size, PAGE_SIZE) ||
+ params->userptr != (unsigned long)params->userptr)
+ return -EINVAL;
+
+ start = params->userptr;
+ if (!IS_ALIGNED(start, PAGE_SIZE) ||
+ check_add_overflow(start, (unsigned long)params->size, &end))
+ return -EINVAL;
+
+ if (!can_do_mlock())
+ return -EPERM;
+
+ if (params->size >> PAGE_SHIFT > INT_MAX)
+ return -E2BIG;
+
+ if (!access_ok((void __user *)start, params->size))
+ return -EFAULT;
+
+ userptr = kzalloc_obj(*userptr);
+ if (!userptr)
+ return -ENOMEM;
+
+ ret = virtio_gpu_userptr_init(vgdev->ddev, file, userptr, params,
+ &virtio_gpu_userptr_ops);
+ if (ret) {
+ kfree(userptr);
+ return ret;
+ }
+
+ mm = current->mm;
+ mmgrab(mm);
+ lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ new_pinned = atomic64_add_return(userptr->npages, &mm->pinned_vm);
+ if (new_pinned < 0 ||
+ (new_pinned > lock_limit && !capable(CAP_IPC_LOCK))) {
+ atomic64_sub(userptr->npages, &mm->pinned_vm);
+ mmdrop(mm);
+ ret = new_pinned < 0 ? -EOVERFLOW : -ENOMEM;
+ goto err_cleanup;
+ }
+ userptr->mm = mm;
+
+ mutex_lock(&userptr->lock);
+ ret = userptr->ops->get_pages(userptr);
+ mutex_unlock(&userptr->lock);
+ if (ret)
+ goto err_cleanup;
+
+ sgt = drm_prime_pages_to_sg(vgdev->ddev, userptr->pages,
+ userptr->npages);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto err_cleanup;
+ }
+
+ userptr->sgt = sgt;
+
+ if (virtio_gpu_use_dma_api(vgdev->vdev)) {
+ enum dma_data_direction dir =
+ (userptr->flags & VIRTGPU_BLOB_FLAG_USE_READONLY) ?
+ DMA_TO_DEVICE : DMA_BIDIRECTIONAL;
+
+ ret = dma_map_sgtable(drm_dev_dma_dev(vgdev->ddev), sgt,
+ dir, 0);
+ if (ret)
+ goto err_cleanup;
+
+ userptr->dma_dir = dir;
+ userptr->dma_mapped = true;
+ }
+
+ ret = virtio_gpu_userptr_get_entries(vgdev, userptr, &ents, &nents);
+ if (ret)
+ goto err_cleanup;
+
+ virtio_gpu_cmd_resource_create_blob(vgdev, &userptr->base, params, ents,
+ nents);
+
+ userptr->base.params = *params;
+ virtio_gpu_add_object_to_restore_list(vgdev, &userptr->base);
+
+ *bo_ptr = &userptr->base;
+ return 0;
+
+err_cleanup:
+ virtio_gpu_cleanup_object(&userptr->base);
+ return ret;
+}
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC PATCH v9 4/4] drm/virtio: wire blob ioctl creation to userptr objects
2026-09-24 9:55 [RFC PATCH v9 0/4] virtio-gpu: Add userptr support for compute workloads Honglei Huang
` (2 preceding siblings ...)
2026-09-24 9:55 ` [RFC PATCH v9 3/4] drm/virtio: implement userptr resource support Honglei Huang
@ 2026-09-24 9:55 ` Honglei Huang
3 siblings, 0 replies; 5+ messages in thread
From: Honglei Huang @ 2026-09-24 9:55 UTC (permalink / raw)
To: dmitry.osipenko, odaki, airlied, kraxel
Cc: gurchetansingh, olvaffe, Ray.Huang, dri-devel, virtualization,
linux-kernel, Honglei Huang
Integrate userptr into the blob resource creation ioctl.
- A non-zero userptr selects virtio_gpu_userptr_create()
- Reject userptr unless blob_mem is VIRTGPU_BLOB_MEM_GUEST
- Reject VIRTGPU_BLOB_FLAG_USE_READONLY unless the device
advertised VIRTIO_GPU_F_BLOB_READONLY
- Advertise VIRTGPU_PARAM_USERPTR and VIRTGPU_PARAM_BLOB_READONLY
- Add VIRTIO_GPU_F_BLOB_READONLY to the driver's features[] array
virtio_has_feature() routes device-specific feature bits through
virtio_check_driver_offered_feature(), which calls BUG() when the
driver queries a bit it never declared in features[]. The
VIRTIO_GPU_F_BLOB_READONLY probe added to virtio_gpu_init() hit
exactly that: every probe crashed regardless of host support because
the bit was missing from features[]. Registering it there fixes the
crash.
Signed-off-by: Honglei Huang <honghuan@amd.com>
---
drivers/gpu/drm/virtio/virtgpu_debugfs.c | 1 +
drivers/gpu/drm/virtio/virtgpu_drv.c | 1 +
drivers/gpu/drm/virtio/virtgpu_ioctl.c | 35 ++++++++++++++++++++----
drivers/gpu/drm/virtio/virtgpu_kms.c | 8 ++++--
4 files changed, 38 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
index 3a68a16b58..b8b9b40584 100644
--- a/drivers/gpu/drm/virtio/virtgpu_debugfs.c
+++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
@@ -55,6 +55,7 @@ static int virtio_gpu_features(struct seq_file *m, void *data)
vgdev->has_resource_assign_uuid);
virtio_gpu_add_bool(m, "blob resources", vgdev->has_resource_blob);
+ virtio_gpu_add_bool(m, "blob readonly", vgdev->has_blob_readonly);
virtio_gpu_add_bool(m, "context init", vgdev->has_context_init);
virtio_gpu_add_int(m, "cap sets", vgdev->num_capsets);
virtio_gpu_add_int(m, "scanouts", vgdev->num_scanouts);
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 2aaa7cb080..f1edd20c37 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -182,6 +182,7 @@ static unsigned int features[] = {
VIRTIO_GPU_F_RESOURCE_BLOB,
VIRTIO_GPU_F_CONTEXT_INIT,
VIRTIO_GPU_F_BLOB_ALIGNMENT,
+ VIRTIO_GPU_F_BLOB_READONLY,
};
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index 3d8e4ccdb7..3dc058e50e 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -36,7 +36,10 @@
#define VIRTGPU_BLOB_FLAG_USE_MASK (VIRTGPU_BLOB_FLAG_USE_MAPPABLE | \
VIRTGPU_BLOB_FLAG_USE_SHAREABLE | \
- VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE)
+ VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE | \
+ VIRTGPU_BLOB_FLAG_USE_READONLY)
+
+#define VIRTGPU_BLOB_HINT_MASK DRM_VIRTGPU_BLOB_FLAG_HINT_DEFER_MAPPING
/* Must be called with &virtio_gpu_fpriv.struct_mutex held. */
static void virtio_gpu_create_context_locked(struct virtio_gpu_device *vgdev,
@@ -122,6 +125,12 @@ static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data,
return -ENOENT;
value = vgdev->blob_alignment;
break;
+ case VIRTGPU_PARAM_USERPTR:
+ value = 1;
+ break;
+ case VIRTGPU_PARAM_BLOB_READONLY:
+ value = vgdev->has_blob_readonly ? 1 : 0;
+ break;
default:
return -EINVAL;
}
@@ -453,11 +462,23 @@ static int verify_blob(struct virtio_gpu_device *vgdev,
if (rc_blob->blob_flags & ~VIRTGPU_BLOB_FLAG_USE_MASK)
return -EINVAL;
+ if (rc_blob->blob_hints & ~VIRTGPU_BLOB_HINT_MASK)
+ return -EINVAL;
+
if (rc_blob->blob_flags & VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE) {
if (!vgdev->has_resource_assign_uuid)
return -EINVAL;
}
+ if (rc_blob->blob_flags & VIRTGPU_BLOB_FLAG_USE_READONLY) {
+ if (!vgdev->has_blob_readonly)
+ return -EINVAL;
+ }
+
+ if (rc_blob->userptr &&
+ rc_blob->blob_mem != VIRTGPU_BLOB_MEM_GUEST)
+ return -EINVAL;
+
switch (rc_blob->blob_mem) {
case VIRTGPU_BLOB_MEM_GUEST:
*guest_blob = true;
@@ -495,6 +516,7 @@ static int verify_blob(struct virtio_gpu_device *vgdev,
params->blob = true;
params->blob_flags = rc_blob->blob_flags;
params->blob_hints = rc_blob->blob_hints;
+ params->userptr = rc_blob->userptr;
if (vgdev->has_blob_alignment &&
!IS_ALIGNED(params->size, vgdev->blob_alignment))
@@ -518,9 +540,10 @@ static int virtio_gpu_resource_create_blob_ioctl(struct drm_device *dev,
struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
struct drm_virtgpu_resource_create_blob *rc_blob = data;
- if (verify_blob(vgdev, vfpriv, ¶ms, rc_blob,
- &guest_blob, &host3d_blob))
- return -EINVAL;
+ ret = verify_blob(vgdev, vfpriv, ¶ms, rc_blob,
+ &guest_blob, &host3d_blob);
+ if (ret)
+ return ret;
if (vgdev->has_virgl_3d)
virtio_gpu_create_context(dev, file);
@@ -538,7 +561,9 @@ static int virtio_gpu_resource_create_blob_ioctl(struct drm_device *dev,
vfpriv->ctx_id, NULL, NULL);
}
- if (guest_blob)
+ if (guest_blob && params.userptr)
+ ret = virtio_gpu_userptr_create(vgdev, file, ¶ms, &bo);
+ else if (guest_blob)
ret = virtio_gpu_object_create(vgdev, ¶ms, &bo, NULL);
else if (!guest_blob && host3d_blob)
ret = virtio_gpu_vram_create(vgdev, ¶ms, &bo);
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 1d4d3bf46a..06c2bded49 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -249,15 +249,19 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
vgdev->blob_alignment = blob_alignment;
}
+ if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_BLOB_READONLY))
+ vgdev->has_blob_readonly = true;
+
DRM_INFO("features: %cvirgl %cedid %cresource_blob %chost_visible",
vgdev->has_virgl_3d ? '+' : '-',
vgdev->has_edid ? '+' : '-',
vgdev->has_resource_blob ? '+' : '-',
vgdev->has_host_visible ? '+' : '-');
- DRM_INFO("features: %ccontext_init %cblob_alignment\n",
+ DRM_INFO("features: %ccontext_init %cblob_alignment %cblob_readonly\n",
vgdev->has_context_init ? '+' : '-',
- vgdev->has_blob_alignment ? '+' : '-');
+ vgdev->has_blob_alignment ? '+' : '-',
+ vgdev->has_blob_readonly ? '+' : '-');
ret = virtio_gpu_find_vqs(vgdev);
if (ret) {
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-24 9:56 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-24 9:55 [RFC PATCH v9 0/4] virtio-gpu: Add userptr support for compute workloads Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 1/4] drm/virtio-gpu: Add VIRTIO_GPU_CAPSET_ROCM capability Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 2/4] drm/virtgpu api: add blob userptr resource Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 3/4] drm/virtio: implement userptr resource support Honglei Huang
2026-09-24 9:55 ` [RFC PATCH v9 4/4] drm/virtio: wire blob ioctl creation to userptr objects Honglei Huang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®