public inbox for drm-ai-reviews@public-inbox.freedesktop.org
 help / color / mirror / Atom feed
* Claude review: drm/virtio: Add support for saving and restoring virtio_gpu_objects
  2026-04-29 20:47 ` [PATCH v8 2/3] drm/virtio: Add support for saving and restoring virtio_gpu_objects dongwon.kim
@ 2026-05-05  1:00   ` Claude Code Review Bot
  0 siblings, 0 replies; 9+ messages in thread
From: Claude Code Review Bot @ 2026-05-05  1:00 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This patch adds the object tracking infrastructure: a per-device restore list protected by a mutex, and logic to re-create objects on the host after hibernation.

**Issue 1 (minor): VRAM objects initialized but never tracked**

In `virtgpu_vram.c`:
```c
INIT_LIST_HEAD(&vram->base.restore_node);
```

VRAM objects get their `restore_node` initialized but are never added to the restore list. The `virtio_gpu_remove_from_restore_list()` call in `virtio_gpu_vram_free()` would be a harmless no-op on an initialized-but-not-linked list node. If VRAM objects are intentionally excluded from restoration (like virgl objects), a brief comment explaining why would be helpful, as the restore_node removal in the free path suggests they might have been intended for tracking.

**Issue 2 (minor): Misleading error code in `virtgpu_dma_buf_obj_resubmit()`**

```c
if (!bo->sgt) {
    DRM_ERROR("no sgt bound to virtio_gpu_object\n");
    return -ENOMEM;
}
```

`-ENOMEM` doesn't describe "no sgt bound". `-EINVAL` or `-ENOENT` would be more accurate.

**Issue 3 (nit): Inconsistent allocation macro**

`virtgpu_dma_buf_obj_resubmit()` uses `kvmalloc_array()` directly:
```c
ents = kvmalloc_array(bo->sgt->nents,
              sizeof(struct virtio_gpu_mem_entry),
              GFP_KERNEL);
```

while `virtio_gpu_object_shmem_init()` uses the `kvmalloc_objs` macro for the same type. Minor inconsistency.

**Issue 4 (nit): Extra blank line**

After `virtgpu_dma_buf_obj_resubmit()` there are two blank lines before `static const struct drm_gem_object_funcs`. Should be one.

**Observation: Restore logic correctness**

The `virtio_gpu_object_restore_all()` logic correctly handles three cases:
1. Imported dma-buf objects → `virtgpu_dma_buf_obj_resubmit()`
2. Blob objects → `shmem_init` + `cmd_resource_create_blob()`
3. Regular (non-blob, non-virgl) objects → `shmem_init` + `cmd_create_resource()` + `object_attach()`

The conditional `if (bo->params.blob || bo->attached)` before `shmem_init` correctly gates the ents/nents initialization to paths that use them. However, `ents` and `nents` are uninitialized at declaration — adding `= NULL` / `= 0` would silence potential compiler warnings and improve clarity.

---

---
Generated by Claude Code Patch Reviewer

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

* [PATCH v9 0/3] Virtio-GPU S4 support
@ 2026-05-26 19:28 dongwon.kim
  2026-05-26 19:28 ` [PATCH v9 1/3] drm/virtio: Freeze and restore hooks to support suspend and resume dongwon.kim
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: dongwon.kim @ 2026-05-26 19:28 UTC (permalink / raw)
  To: dri-devel, airlied, kraxel, dmitry.osipenko

From: Dongwon Kim <dongwon.kim@intel.com>

This patch series implements the .freeze and .restore hooks for the virtio-gpu
driver, alongside a PM notifier to properly handle object tracking and 
restoration for S4 (hibernation) scenarios.

The first patch adds the `virtgpu_freeze` and `virtgpu_restore` functions. 
These functions orchestrate flushing pending work, safely deleting virtio 
queues before suspension once they are empty, and recreating them during 
the restoration process.

The second patch implements a tracking and recreation mechanism for active 
`virtio_gpu_object` instances. This is necessary because the host hypervisor 
(e.g., QEMU) terminates or resets the backend during a full hibernation cycle, 
wiping out all previously established graphics resources and causing post-resume 
errors in the guest.

The third patch integrates a PM notifier block into the driver. It captures 
the PM_HIBERNATION_PREPARE phase to check compatibility (blocking S4 if Virgl 
3D is active) and sets a state flag. This flag instructs `virtgpu_restore` to 
fully re-submit and restore active GPU objects immediately after the fresh 
virtqueues are reconfigured.

These changes ensure that the virtio-gpu driver can cleanly survive guest 
hibernation without experiencing host-side resource loss.

v2: 10ms sleep is added in virtgpu_freeze to avoid the situation
    the driver is locked up during resumption.

v3: Plain 10ms delay (v2) is replaced with wait calls which wait until
    the virtio queue is empty.
    (Dmitry Osipenko)

v4: New version of patchset only covers S4 case because loss of resources in S3
    case can be avoided by skipping virtio-gpu-reset in QEMU (hw/display/virtio-gpu.c).
    To skip virtio-gpu-reset (soft-reset), virtio-gpu-pci device should be attached to
    PCIE bus AND a PCIE option, 'x-pcie-pm-no-soft-reset' should added and set to 'true'.
    (e.g. -device virtio-gpu-pci,bus=port,x-pcie-pm-no-soft-reset=true)

v5: Remove virtio_gpu_object from the restore list before freeing the object
    to prevent an use-after-free situation.
    (Nirmoy Das)

    Protect restore list operations with a spinlock
    (Nirmoy Das)

    Move restore list node into virtio_gpu_bo struct to reduce memory usage
    (Dmitry Osipenko)

    Remove unused header - drm_atomic_helper.h
    (Dmitry Osipenko)

v6: Include object backed by imported dmabuf
    (Dmitry Osipenko)

    Not storing virgl objects in the restore_list as virgl 3D objects are not
    recoverable.
    (Dmitry Osipenko)

    Change the name 'list',a node in restore_list to 'restore_node'
    (Nirmoy Das)

    Use mutex instead of spinlock when updating restore_list
    (Nirmoy Das)

    Initialize restore_node when virtio_gpu_object is created - this is to
    check if the node is in the list with 'list_empty' before removing it.

    Restoring objects in the PM notifier is too late, as virtio-gpu
    message communication begins in virtgpu_restore once virtqueues
    are re-established. To address this, a 'hibernation' flag is set
    during the PM_HIBERNATION_PREPARE phase in the notifier. This flag
    is then used in virtgpu_restore to detect if the system is resuming
    from S4, allowing objects to be recovered immediately after virtqueues
    are reconfigured.

v7: Add a helper, virtio_gpu_add_object_to_restore_list
    (Dmitry Osipenko)

    Unreference all objects before hibernation so they can be removed
    on the host side, since they will be fully restored anyway. This
    prevents the situation where host-side hibernation fails (leaving
    all associated resources still alive) while the virtio-gpu driver
    still attempts to restore those objects.
    (Dmitry Osipenko)

v8: Add drm_print.h to virtgpu_prime.c

v9: Move duplicate wait_event_timeout blocks into a single
    virtio_gpu_wait_queue helper function.
    (Dmitry Osipenko)

    Handle timeouts gracefully by returning -ETIMEDOUT to abort
    the freeze operation if the host hangs.
    (Dmitry Osipenko)

Dongwon Kim (3):
  drm/virtio: Freeze and restore hooks to support suspend and resume
  drm/virtio: Add support for saving and restoring virtio_gpu_objects
  drm/virtio: Add PM notifier to restore objects after hibernation

 drivers/gpu/drm/virtio/virtgpu_drv.c    | 80 ++++++++++++++++++++++-
 drivers/gpu/drm/virtio/virtgpu_drv.h    | 25 ++++++-
 drivers/gpu/drm/virtio/virtgpu_kms.c    | 54 +++++++++++++--
 drivers/gpu/drm/virtio/virtgpu_object.c | 87 ++++++++++++++++++++++++-
 drivers/gpu/drm/virtio/virtgpu_prime.c  | 45 ++++++++++++-
 drivers/gpu/drm/virtio/virtgpu_vq.c     | 30 ++++++++-
 drivers/gpu/drm/virtio/virtgpu_vram.c   |  5 +-
 7 files changed, 312 insertions(+), 14 deletions(-)

-- 
2.34.1


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

* [PATCH v9 1/3] drm/virtio: Freeze and restore hooks to support suspend and resume
  2026-05-26 19:28 [PATCH v9 0/3] Virtio-GPU S4 support dongwon.kim
@ 2026-05-26 19:28 ` dongwon.kim
  2026-05-27  4:17   ` Claude review: " Claude Code Review Bot
  2026-05-26 19:28 ` [PATCH v9 2/3] drm/virtio: Add support for saving and restoring virtio_gpu_objects dongwon.kim
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: dongwon.kim @ 2026-05-26 19:28 UTC (permalink / raw)
  To: dri-devel, airlied, kraxel, dmitry.osipenko

From: Dongwon Kim <dongwon.kim@intel.com>

virtio device needs to delete before VM suspend happens
then reinitialize all virtqueues again upon resume

Suggested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
Cc: Nirmoy Das <nirmoyd@nvidia.com>
Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
---
v2: 10ms sleep was added in virtgpu_freeze to avoid the situation
    the driver is locked up during resumption.

v3: Plain 10ms delay was replaced with wait calls which wait until
    the virtio queue is empty.
    (Dmitry Osipenko)

v4: Change wait_event to wait_event_timeout to prevent permanent wait
    (Nirmoy Das)

v5: Move duplicate wait_event_timeout blocks into a single
    virtio_gpu_wait_queue helper function.
    (Dmitry Osipenko)

    Handle timeouts gracefully by returning -ETIMEDOUT to abort
    the freeze operation if the host hangs.
    (Dmitry Osipenko)
---
 drivers/gpu/drm/virtio/virtgpu_drv.c | 68 +++++++++++++++++++++++++++-
 drivers/gpu/drm/virtio/virtgpu_drv.h |  2 +
 drivers/gpu/drm/virtio/virtgpu_kms.c | 23 +++++++---
 drivers/gpu/drm/virtio/virtgpu_vq.c  | 17 +++++++
 4 files changed, 103 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 812ee3f5e4aa..fd3fbc7474dd 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -165,6 +165,68 @@ static unsigned int features[] = {
 	VIRTIO_GPU_F_CONTEXT_INIT,
 	VIRTIO_GPU_F_BLOB_ALIGNMENT,
 };
+
+#ifdef CONFIG_PM_SLEEP
+static int virtgpu_freeze(struct virtio_device *vdev)
+{
+	struct drm_device *dev = vdev->priv;
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	int error;
+
+	error = drm_mode_config_helper_suspend(dev);
+	if (error) {
+		DRM_ERROR("suspend error %d\n", error);
+		return error;
+	}
+
+	flush_work(&vgdev->obj_free_work);
+	flush_work(&vgdev->ctrlq.dequeue_work);
+	flush_work(&vgdev->cursorq.dequeue_work);
+	flush_work(&vgdev->config_changed_work);
+
+	error = virtio_gpu_wait_queue(&vgdev->ctrlq, vgdev->ctrlq.vq->num_max);
+
+	if (error) {
+		DRM_ERROR("suspend error %d\n", error);
+		return error;
+	}
+
+	error = virtio_gpu_wait_queue(&vgdev->cursorq, vgdev->cursorq.vq->num_max);
+
+	if (error) {
+		DRM_ERROR("suspend error %d\n", error);
+		return error;
+	}
+
+	vdev->config->del_vqs(vdev);
+
+	return 0;
+}
+
+static int virtgpu_restore(struct virtio_device *vdev)
+{
+	struct drm_device *dev = vdev->priv;
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	int error;
+
+	error = virtio_gpu_find_vqs(vgdev);
+	if (error) {
+		DRM_ERROR("failed to find virt queues\n");
+		return error;
+	}
+
+	virtio_device_ready(vdev);
+
+	error = drm_mode_config_helper_resume(dev);
+	if (error) {
+		DRM_ERROR("resume error %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+#endif
+
 static struct virtio_driver virtio_gpu_driver = {
 	.feature_table = features,
 	.feature_table_size = ARRAY_SIZE(features),
@@ -173,7 +235,11 @@ static struct virtio_driver virtio_gpu_driver = {
 	.probe = virtio_gpu_probe,
 	.remove = virtio_gpu_remove,
 	.shutdown = virtio_gpu_shutdown,
-	.config_changed = virtio_gpu_config_changed
+	.config_changed = virtio_gpu_config_changed,
+#ifdef CONFIG_PM_SLEEP
+	.freeze = virtgpu_freeze,
+	.restore = virtgpu_restore,
+#endif
 };
 
 static int __init virtio_gpu_driver_init(void)
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 7449907754a4..c630c75e308b 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -303,6 +303,7 @@ void virtio_gpu_deinit(struct drm_device *dev);
 void virtio_gpu_release(struct drm_device *dev);
 int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file);
 void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file);
+int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev);
 
 /* virtgpu_gem.c */
 int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
@@ -424,6 +425,7 @@ void virtio_gpu_dequeue_ctrl_func(struct work_struct *work);
 void virtio_gpu_dequeue_cursor_func(struct work_struct *work);
 void virtio_gpu_panic_notify(struct virtio_gpu_device *vgdev);
 void virtio_gpu_notify(struct virtio_gpu_device *vgdev);
+int virtio_gpu_wait_queue(struct virtio_gpu_queue *vgvq, int num_elem);
 
 int
 virtio_gpu_cmd_resource_assign_uuid(struct virtio_gpu_device *vgdev,
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index cfde9f573df6..82baf1b3717e 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -115,15 +115,28 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev,
 	vgdev->num_capsets = num_capsets;
 }
 
-int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
+int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev)
 {
 	struct virtqueue_info vqs_info[] = {
 		{ "control", virtio_gpu_ctrl_ack },
 		{ "cursor", virtio_gpu_cursor_ack },
 	};
-	struct virtio_gpu_device *vgdev;
-	/* this will expand later */
 	struct virtqueue *vqs[2];
+	int ret;
+
+	ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL);
+	if (ret)
+		return ret;
+
+	vgdev->ctrlq.vq = vqs[0];
+	vgdev->cursorq.vq = vqs[1];
+
+	return 0;
+}
+
+int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
+{
+	struct virtio_gpu_device *vgdev;
 	u32 num_scanouts, num_capsets, blob_alignment;
 	int ret = 0;
 
@@ -215,13 +228,11 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
 		 vgdev->has_context_init ? '+' : '-',
 		 vgdev->has_blob_alignment ? '+' : '-');
 
-	ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL);
+	ret = virtio_gpu_find_vqs(vgdev);
 	if (ret) {
 		DRM_ERROR("failed to find virt queues\n");
 		goto err_vqs;
 	}
-	vgdev->ctrlq.vq = vqs[0];
-	vgdev->cursorq.vq = vqs[1];
 	ret = virtio_gpu_alloc_vbufs(vgdev);
 	if (ret) {
 		DRM_ERROR("failed to alloc vbufs\n");
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 67865810a2e7..22bf11497f16 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -369,6 +369,23 @@ static int virtio_gpu_panic_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
 	return 0;
 }
 
+int virtio_gpu_wait_queue(struct virtio_gpu_queue *vgvq, int num_elem)
+{
+	int ret;
+
+	/* Cap num_elem at the queue's maximum capacity */
+	num_elem = min(num_elem, (int)vgvq->vq->num_max);
+
+	/* Wait up to 5 seconds for enough free slots to become available */
+	ret = wait_event_timeout(vgvq->ack_queue,
+				 vgvq->vq->num_free >= num_elem,
+				 5 * HZ);
+	if (ret == 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
 static int virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
 				     struct virtio_gpu_vbuffer *vbuf,
 				     struct virtio_gpu_fence *fence,
-- 
2.34.1


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

* [PATCH v9 2/3] drm/virtio: Add support for saving and restoring virtio_gpu_objects
  2026-05-26 19:28 [PATCH v9 0/3] Virtio-GPU S4 support dongwon.kim
  2026-05-26 19:28 ` [PATCH v9 1/3] drm/virtio: Freeze and restore hooks to support suspend and resume dongwon.kim
@ 2026-05-26 19:28 ` dongwon.kim
  2026-05-27  4:17   ` Claude review: " Claude Code Review Bot
  2026-05-26 19:28 ` [PATCH v9 3/3] drm/virtio: Add PM notifier to restore objects after hibernation dongwon.kim
  2026-05-27  4:17 ` Claude review: Virtio-GPU S4 support Claude Code Review Bot
  3 siblings, 1 reply; 9+ messages in thread
From: dongwon.kim @ 2026-05-26 19:28 UTC (permalink / raw)
  To: dri-devel, airlied, kraxel, dmitry.osipenko

From: Dongwon Kim <dongwon.kim@intel.com>

When the host KVM/QEMU resumes from hibernation, it loses all graphics
resources previously submitted by the guest OS, as the QEMU process is
terminated during the suspend-resume cycle. This leads to invalid resource
errors when the guest OS attempts to interact with the host using those
resources after resumption.

To resolve this, the virtio-gpu driver now tracks all active virtio_gpu_objects
and provides a mechanism to restore them by re-submitting the objects to QEMU
when needed (e.g., during resume from hibernation).

Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
Cc: Nirmoy Das <nirmoyd@nvidia.com>
Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
---
v2: - Attach backing is done if bo->attached was set before

v3: - Restoration is no longer triggered via .restore; instead, it is handled
      by a PM notifier only during hibernation.

v4: - Remove virtio_gpu_object from the restore list before freeing the object
      to prevent an use-after-free situation.
      (Nirmoy Das)

    - Protect restore list operations with a spinlock
      (Nirmoy Das)

    - Initialize ret with 0 in virtio_gpu_object_restore_all
      (Nirmoy Das)

    - Move restore list node into virtio_gpu_bo struct to reduce memory usage
      (Dmitry Osipenko)

v5: - Include object backed by imported dmabuf
      (Dmitry Osipenko)

    - Not storing virgl objects in the restore_list as virgl 3D objects are not
      recoverable.
      (Dmitry Osipenko)

    - Change the name 'list',a node in restore_list to 'restore_node'
      (Nirmoy Das)

    - Use mutex instead of spinlock when updating restore_list
      (Nirmoy Das)

    - Initialize restore_node when virtio_gpu_object is created - this is to
      determine whether the object should be removed from the restore_list with
      'list_empty' function when it is time to free the object as not all objects
      will be added to the list.

v6: - Add a helper, virtio_gpu_add_object_to_restore_list
      (Dmitry Osipenko)

v7: - Add drm_print.h

v8: - Remove BO from restore list right before unref is called
---
 drivers/gpu/drm/virtio/virtgpu_drv.h    | 15 ++++++
 drivers/gpu/drm/virtio/virtgpu_kms.c    |  3 ++
 drivers/gpu/drm/virtio/virtgpu_object.c | 70 +++++++++++++++++++++++++
 drivers/gpu/drm/virtio/virtgpu_prime.c  | 43 +++++++++++++++
 drivers/gpu/drm/virtio/virtgpu_vram.c   |  3 ++
 5 files changed, 134 insertions(+)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index c630c75e308b..647e0f5f4f8e 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -99,6 +99,10 @@ struct virtio_gpu_object {
 
 	int uuid_state;
 	uuid_t uuid;
+
+	/* for restoration of objects after hibernation */
+	struct virtio_gpu_object_params params;
+	struct list_head restore_node;
 };
 #define gem_to_virtio_gpu_obj(gobj) \
 	container_of((gobj), struct virtio_gpu_object, base.base)
@@ -267,6 +271,8 @@ struct virtio_gpu_device {
 	struct work_struct obj_free_work;
 	spinlock_t obj_free_lock;
 	struct list_head obj_free_list;
+	struct mutex obj_restore_lock;
+	struct list_head obj_restore_list;
 
 	struct virtio_gpu_drv_capset *capsets;
 	uint32_t num_capsets;
@@ -472,6 +478,7 @@ void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev,
 				    u64 fence_id);
 
 /* virtgpu_object.c */
+void virtio_gpu_remove_from_restore_list(struct virtio_gpu_object *bo);
 void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo);
 struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
 						size_t size);
@@ -484,6 +491,12 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo);
 
 int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev,
 			       uint32_t *resid);
+
+void virtio_gpu_add_object_to_restore_list(struct virtio_gpu_device *vgdev,
+					   struct virtio_gpu_object *bo);
+
+int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev);
+
 /* virtgpu_prime.c */
 int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev,
 				    struct virtio_gpu_object *bo);
@@ -498,6 +511,8 @@ int virtgpu_dma_buf_import_sgt(struct virtio_gpu_mem_entry **ents,
 			       unsigned int *nents,
 			       struct virtio_gpu_object *bo,
 			       struct dma_buf_attachment *attach);
+int virtgpu_dma_buf_obj_resubmit(struct virtio_gpu_device *vgdev,
+				 struct virtio_gpu_object *bo);
 
 /* virtgpu_debugfs.c */
 void virtio_gpu_debugfs_init(struct drm_minor *minor);
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 82baf1b3717e..9601f310875d 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -171,6 +171,8 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
 		  virtio_gpu_array_put_free_work);
 	INIT_LIST_HEAD(&vgdev->obj_free_list);
 	spin_lock_init(&vgdev->obj_free_lock);
+	INIT_LIST_HEAD(&vgdev->obj_restore_list);
+	mutex_init(&vgdev->obj_restore_lock);
 
 #ifdef __LITTLE_ENDIAN
 	if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_VIRGL))
@@ -307,6 +309,7 @@ void virtio_gpu_deinit(struct drm_device *dev)
 	flush_work(&vgdev->config_changed_work);
 	virtio_reset_device(vgdev->vdev);
 	vgdev->vdev->config->del_vqs(vgdev->vdev);
+	mutex_destroy(&vgdev->obj_restore_lock);
 }
 
 void virtio_gpu_release(struct drm_device *dev)
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index ec9efacc6919..a1ea65f7b1de 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -63,6 +63,15 @@ static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t
 		ida_free(&vgdev->resource_ida, id - 1);
 }
 
+void virtio_gpu_remove_from_restore_list(struct virtio_gpu_object *bo)
+{
+	struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
+
+	mutex_lock(&vgdev->obj_restore_lock);
+	list_del_init(&bo->restore_node);
+	mutex_unlock(&vgdev->obj_restore_lock);
+}
+
 void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo)
 {
 	struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
@@ -94,6 +103,7 @@ static void virtio_gpu_free_object(struct drm_gem_object *obj)
 	struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
 
 	if (bo->created) {
+		virtio_gpu_remove_from_restore_list(bo);
 		virtio_gpu_cmd_unref_resource(vgdev, bo);
 		virtio_gpu_notify(vgdev);
 		/* completion handler calls virtio_gpu_cleanup_object() */
@@ -220,6 +230,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
 		return PTR_ERR(shmem_obj);
 	bo = gem_to_virtio_gpu_obj(&shmem_obj->base);
 
+	INIT_LIST_HEAD(&bo->restore_node);
+
 	ret = virtio_gpu_resource_id_get(vgdev, &bo->hw_res_handle);
 	if (ret < 0)
 		goto err_free_gem;
@@ -258,6 +270,12 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
 		virtio_gpu_object_attach(vgdev, bo, ents, nents);
 	}
 
+	if (!params->virgl) {
+		/* store non-virgl object with its param to the restore list */
+		bo->params = *params;
+		virtio_gpu_add_object_to_restore_list(vgdev, bo);
+	}
+
 	*bo_ptr = bo;
 	return 0;
 
@@ -271,3 +289,55 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
 	drm_gem_shmem_free(shmem_obj);
 	return ret;
 }
+
+void virtio_gpu_add_object_to_restore_list(struct virtio_gpu_device *vgdev,
+					   struct virtio_gpu_object *bo)
+{
+	mutex_lock(&vgdev->obj_restore_lock);
+	list_add_tail(&bo->restore_node, &vgdev->obj_restore_list);
+	mutex_unlock(&vgdev->obj_restore_lock);
+}
+
+int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev)
+{
+	struct virtio_gpu_object *bo, *tmp;
+	struct virtio_gpu_mem_entry *ents;
+	unsigned int nents;
+	int ret = 0;
+
+	mutex_lock(&vgdev->obj_restore_lock);
+	list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list,
+				 restore_node) {
+		if (drm_gem_is_imported(&bo->base.base)) {
+			ret = virtgpu_dma_buf_obj_resubmit(vgdev, bo);
+			if (ret)
+				break;
+
+			continue;
+		}
+
+		if (bo->params.blob || bo->attached) {
+			ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents,
+							   &nents);
+			if (ret)
+				break;
+		}
+
+		if (bo->params.blob) {
+			virtio_gpu_cmd_resource_create_blob(vgdev, bo,
+							    &bo->params,
+							    ents, nents);
+		} else {
+			virtio_gpu_cmd_create_resource(vgdev, bo, &bo->params,
+						       NULL, NULL);
+			if (bo->attached) {
+				bo->attached = false;
+				virtio_gpu_object_attach(vgdev, bo, ents,
+							 nents);
+			}
+		}
+	}
+	mutex_unlock(&vgdev->obj_restore_lock);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c
index 70b3b836e1c9..27eaab9f5cfa 100644
--- a/drivers/gpu/drm/virtio/virtgpu_prime.c
+++ b/drivers/gpu/drm/virtio/virtgpu_prime.c
@@ -23,6 +23,7 @@
  */
 
 #include <drm/drm_prime.h>
+#include <drm/drm_print.h>
 #include <linux/virtio_dma_buf.h>
 
 #include "virtgpu_drv.h"
@@ -216,6 +217,7 @@ static void virtgpu_dma_buf_free_obj(struct drm_gem_object *obj)
 	}
 
 	if (bo->created) {
+		virtio_gpu_remove_from_restore_list(bo);
 		virtio_gpu_cmd_unref_resource(vgdev, bo);
 		virtio_gpu_notify(vgdev);
 		return;
@@ -262,6 +264,12 @@ static int virtgpu_dma_buf_init_obj(struct drm_device *dev,
 	dma_buf_unpin(attach);
 	dma_resv_unlock(resv);
 
+	/* store the dmabuf imported object with its params to
+	 * the restore list
+	 */
+	bo->params = params;
+	virtio_gpu_add_object_to_restore_list(vgdev, bo);
+
 	return 0;
 
 err_import:
@@ -272,6 +280,39 @@ static int virtgpu_dma_buf_init_obj(struct drm_device *dev,
 	return ret;
 }
 
+int virtgpu_dma_buf_obj_resubmit(struct virtio_gpu_device *vgdev,
+				 struct virtio_gpu_object *bo)
+{
+	struct virtio_gpu_mem_entry *ents;
+	struct scatterlist *sl;
+	int i;
+
+	if (!bo->sgt) {
+		DRM_ERROR("no sgt bound to virtio_gpu_object\n");
+		return -ENOMEM;
+	}
+
+	ents = kvmalloc_array(bo->sgt->nents,
+			      sizeof(struct virtio_gpu_mem_entry),
+			      GFP_KERNEL);
+	if (!ents) {
+		DRM_ERROR("failed to allocate ent list\n");
+		return -ENOMEM;
+	}
+
+	for_each_sgtable_dma_sg(bo->sgt, sl, i) {
+		ents[i].addr = cpu_to_le64(sg_dma_address(sl));
+		ents[i].length = cpu_to_le32(sg_dma_len(sl));
+		ents[i].padding = 0;
+	}
+
+	virtio_gpu_cmd_resource_create_blob(vgdev, bo, &bo->params,
+					    ents, bo->sgt->nents);
+
+	return 0;
+}
+
+
 static const struct drm_gem_object_funcs virtgpu_gem_dma_buf_funcs = {
 	.free = virtgpu_dma_buf_free_obj,
 };
@@ -317,6 +358,8 @@ struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev,
 	if (!bo)
 		return ERR_PTR(-ENOMEM);
 
+	INIT_LIST_HEAD(&bo->restore_node);
+
 	obj = &bo->base.base;
 	obj->resv = buf->resv;
 	obj->funcs = &virtgpu_gem_dma_buf_funcs;
diff --git a/drivers/gpu/drm/virtio/virtgpu_vram.c b/drivers/gpu/drm/virtio/virtgpu_vram.c
index 4ae3cbc35dd3..9ad11c7f1194 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vram.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vram.c
@@ -20,6 +20,7 @@ static void virtio_gpu_vram_free(struct drm_gem_object *obj)
 		if (unmap)
 			virtio_gpu_cmd_unmap(vgdev, bo);
 
+		virtio_gpu_remove_from_restore_list(bo);
 		virtio_gpu_cmd_unref_resource(vgdev, bo);
 		virtio_gpu_notify(vgdev);
 		return;
@@ -207,6 +208,8 @@ int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev,
 	obj = &vram->base.base.base;
 	obj->funcs = &virtio_gpu_vram_funcs;
 
+	INIT_LIST_HEAD(&vram->base.restore_node);
+
 	params->size = PAGE_ALIGN(params->size);
 	drm_gem_private_object_init(vgdev->ddev, obj, params->size);
 
-- 
2.34.1


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

* [PATCH v9 3/3] drm/virtio: Add PM notifier to restore objects after hibernation
  2026-05-26 19:28 [PATCH v9 0/3] Virtio-GPU S4 support dongwon.kim
  2026-05-26 19:28 ` [PATCH v9 1/3] drm/virtio: Freeze and restore hooks to support suspend and resume dongwon.kim
  2026-05-26 19:28 ` [PATCH v9 2/3] drm/virtio: Add support for saving and restoring virtio_gpu_objects dongwon.kim
@ 2026-05-26 19:28 ` dongwon.kim
  2026-05-27  4:17   ` Claude review: " Claude Code Review Bot
  2026-05-27  4:17 ` Claude review: Virtio-GPU S4 support Claude Code Review Bot
  3 siblings, 1 reply; 9+ messages in thread
From: dongwon.kim @ 2026-05-26 19:28 UTC (permalink / raw)
  To: dri-devel, airlied, kraxel, dmitry.osipenko

From: Dongwon Kim <dongwon.kim@intel.com>

Register a PM notifier in virtio-gpu to handle suspend/hibernate
events. On PM_POST_HIBERNATION, restore all GPU objects so that the
driver can properly recover after resume.

Suggested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
---
v2: Remove unused header - drm_atomic_helper.h
    (Dmitry Osipenko)

v3: Objects for virgl usecase can't be recovered after resume so
    blocking S4 when virgl is enabled
    (Dmitry Osipenko)

v4: Restoring objects in the PM notifier is too late, as virtio-gpu
    message communication begins in virtgpu_restore once virtqueues
    are re-established. To address this, a 'hibernation' flag is set
    during the PM_HIBERNATION_PREPARE phase in the notifier. This flag
    is then used in virtgpu_restore to detect if the system is resuming
    from S4, allowing objects to be recovered immediately after virtqueues
    are reconfigured.

v5: Unreference all objects before hibernation so they can be removed
    on the host side, since they will be fully restored anyway. This
    prevents the situation where host-side hibernation fails (leaving
    all associated resources still alive) while the virtio-gpu driver
    still attempts to restore those objects.
    (Dmitry Osipenko)
---
 drivers/gpu/drm/virtio/virtgpu_drv.c    | 12 +++++++++++
 drivers/gpu/drm/virtio/virtgpu_drv.h    |  8 ++++++-
 drivers/gpu/drm/virtio/virtgpu_kms.c    | 28 +++++++++++++++++++++++++
 drivers/gpu/drm/virtio/virtgpu_object.c | 17 ++++++++++++++-
 drivers/gpu/drm/virtio/virtgpu_prime.c  |  2 +-
 drivers/gpu/drm/virtio/virtgpu_vq.c     | 13 +++++++++---
 drivers/gpu/drm/virtio/virtgpu_vram.c   |  2 +-
 7 files changed, 75 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index fd3fbc7474dd..707e648f9367 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -179,6 +179,9 @@ static int virtgpu_freeze(struct virtio_device *vdev)
 		return error;
 	}
 
+	if (vgdev->hibernation)
+		virtio_gpu_object_unref_all(vgdev);
+
 	flush_work(&vgdev->obj_free_work);
 	flush_work(&vgdev->ctrlq.dequeue_work);
 	flush_work(&vgdev->cursorq.dequeue_work);
@@ -217,6 +220,15 @@ static int virtgpu_restore(struct virtio_device *vdev)
 
 	virtio_device_ready(vdev);
 
+	if (vgdev->hibernation) {
+		vgdev->hibernation = false;
+		error = virtio_gpu_object_restore_all(vgdev);
+		if (error) {
+			DRM_ERROR("Failed to recover virtio-gpu objects\n");
+			return error;
+		}
+	}
+
 	error = drm_mode_config_helper_resume(dev);
 	if (error) {
 		DRM_ERROR("resume error %d\n", error);
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 647e0f5f4f8e..505533653729 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -263,6 +263,7 @@ struct virtio_gpu_device {
 	bool has_host_visible;
 	bool has_context_init;
 	bool has_blob_alignment;
+	bool hibernation;
 	struct virtio_shm_region host_visible_region;
 	struct drm_mm host_visible_mm;
 
@@ -280,6 +281,8 @@ struct virtio_gpu_device {
 	struct list_head cap_cache;
 	uint32_t blob_alignment;
 
+	struct notifier_block pm_nb;
+
 	/* protects uuid state when exporting */
 	spinlock_t resource_export_lock;
 	/* protects map state and host_visible_mm */
@@ -345,7 +348,8 @@ void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
 				    struct virtio_gpu_object_array *objs,
 				    struct virtio_gpu_fence *fence);
 void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
-				   struct virtio_gpu_object *bo);
+				   struct virtio_gpu_object *bo,
+				   int no_cb);
 int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
 					     uint64_t offset,
 					     uint32_t width, uint32_t height,
@@ -497,6 +501,8 @@ void virtio_gpu_add_object_to_restore_list(struct virtio_gpu_device *vgdev,
 
 int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev);
 
+void virtio_gpu_object_unref_all(struct virtio_gpu_device *vgdev);
+
 /* virtgpu_prime.c */
 int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev,
 				    struct virtio_gpu_object *bo);
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 9601f310875d..9cc88662b757 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -26,6 +26,8 @@
 #include <linux/virtio.h>
 #include <linux/virtio_config.h>
 #include <linux/virtio_ring.h>
+#include <linux/suspend.h>
+#include <linux/pm_runtime.h>
 
 #include <drm/drm_file.h>
 #include <drm/drm_managed.h>
@@ -134,6 +136,25 @@ int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev)
 	return 0;
 }
 
+static int virtio_gpu_pm_notifier(struct notifier_block *nb, unsigned long mode,
+				  void *data)
+{
+	struct virtio_gpu_device *vgdev = container_of(nb,
+						struct virtio_gpu_device,
+						pm_nb);
+
+	if (mode == PM_HIBERNATION_PREPARE) {
+		if (vgdev->has_virgl_3d) {
+			DRM_ERROR("S4 not allowed when VIRGL is enabled\n");
+			return notifier_from_errno(-EPERM);
+		}
+
+		vgdev->hibernation = true;
+	}
+
+	return NOTIFY_DONE;
+}
+
 int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
 {
 	struct virtio_gpu_device *vgdev;
@@ -278,6 +299,12 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
 		wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending,
 				   5 * HZ);
 	}
+
+	vgdev->pm_nb.notifier_call = virtio_gpu_pm_notifier;
+	ret = register_pm_notifier(&vgdev->pm_nb);
+	if (ret)
+		goto err_scanouts;
+
 	return 0;
 
 err_scanouts:
@@ -310,6 +337,7 @@ void virtio_gpu_deinit(struct drm_device *dev)
 	virtio_reset_device(vgdev->vdev);
 	vgdev->vdev->config->del_vqs(vgdev->vdev);
 	mutex_destroy(&vgdev->obj_restore_lock);
+	unregister_pm_notifier(&vgdev->pm_nb);
 }
 
 void virtio_gpu_release(struct drm_device *dev)
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index a1ea65f7b1de..8d12633e6f4c 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -104,7 +104,7 @@ static void virtio_gpu_free_object(struct drm_gem_object *obj)
 
 	if (bo->created) {
 		virtio_gpu_remove_from_restore_list(bo);
-		virtio_gpu_cmd_unref_resource(vgdev, bo);
+		virtio_gpu_cmd_unref_resource(vgdev, bo, false);
 		virtio_gpu_notify(vgdev);
 		/* completion handler calls virtio_gpu_cleanup_object() */
 		return;
@@ -341,3 +341,18 @@ int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev)
 
 	return ret;
 }
+
+void virtio_gpu_object_unref_all(struct virtio_gpu_device *vgdev)
+{
+	struct virtio_gpu_object *bo, *tmp;
+
+	mutex_lock(&vgdev->obj_restore_lock);
+	list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list,
+				 restore_node)
+		if (bo->created) {
+			virtio_gpu_cmd_unref_resource(vgdev, bo, true);
+			virtio_gpu_notify(vgdev);
+		}
+
+	mutex_unlock(&vgdev->obj_restore_lock);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c
index 27eaab9f5cfa..56686c453763 100644
--- a/drivers/gpu/drm/virtio/virtgpu_prime.c
+++ b/drivers/gpu/drm/virtio/virtgpu_prime.c
@@ -218,7 +218,7 @@ static void virtgpu_dma_buf_free_obj(struct drm_gem_object *obj)
 
 	if (bo->created) {
 		virtio_gpu_remove_from_restore_list(bo);
-		virtio_gpu_cmd_unref_resource(vgdev, bo);
+		virtio_gpu_cmd_unref_resource(vgdev, bo, false);
 		virtio_gpu_notify(vgdev);
 		return;
 	}
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 22bf11497f16..fbd03e7abe66 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -643,14 +643,21 @@ static void virtio_gpu_cmd_unref_cb(struct virtio_gpu_device *vgdev,
 }
 
 void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
-				   struct virtio_gpu_object *bo)
+				   struct virtio_gpu_object *bo,
+				   int no_cb)
 {
 	struct virtio_gpu_resource_unref *cmd_p;
 	struct virtio_gpu_vbuffer *vbuf;
 	int ret;
 
-	cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p),
-					virtio_gpu_cmd_unref_cb);
+	if (no_cb) {
+		cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p),
+						NULL);
+	} else {
+		cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p),
+						virtio_gpu_cmd_unref_cb);
+	}
+
 	memset(cmd_p, 0, sizeof(*cmd_p));
 
 	cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNREF);
diff --git a/drivers/gpu/drm/virtio/virtgpu_vram.c b/drivers/gpu/drm/virtio/virtgpu_vram.c
index 9ad11c7f1194..e2e799b42c61 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vram.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vram.c
@@ -21,7 +21,7 @@ static void virtio_gpu_vram_free(struct drm_gem_object *obj)
 			virtio_gpu_cmd_unmap(vgdev, bo);
 
 		virtio_gpu_remove_from_restore_list(bo);
-		virtio_gpu_cmd_unref_resource(vgdev, bo);
+		virtio_gpu_cmd_unref_resource(vgdev, bo, false);
 		virtio_gpu_notify(vgdev);
 		return;
 	}
-- 
2.34.1


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

* Claude review: Virtio-GPU S4 support
  2026-05-26 19:28 [PATCH v9 0/3] Virtio-GPU S4 support dongwon.kim
                   ` (2 preceding siblings ...)
  2026-05-26 19:28 ` [PATCH v9 3/3] drm/virtio: Add PM notifier to restore objects after hibernation dongwon.kim
@ 2026-05-27  4:17 ` Claude Code Review Bot
  3 siblings, 0 replies; 9+ messages in thread
From: Claude Code Review Bot @ 2026-05-27  4:17 UTC (permalink / raw)
  To: dri-devel-reviews

Overall Series Review

Subject: Virtio-GPU S4 support
Author: dongwon.kim@intel.com
Patches: 4
Reviewed: 2026-05-27T14:17:08.027796

---

This v9 series adds S4 (hibernation) support for the virtio-gpu driver across 3 patches: freeze/restore hooks, object tracking/restoration, and a PM notifier. The architecture is sound — PM_HIBERNATION_PREPARE sets a flag, freeze unrefs all tracked objects to clean the host side, and restore re-submits them to a freshly started QEMU. The series has been through extensive review already (v9), and the core design is solid.

There are a few issues worth raising: missing error recovery in the freeze path (leaving DRM suspended on timeout), an unnecessary include, use of `int` instead of `bool` for the `no_cb` parameter, and VRAM objects silently not being restored. None are showstoppers, but the error recovery gap in freeze is worth fixing.

---

---
Generated by Claude Code Patch Reviewer

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

* Claude review: drm/virtio: Freeze and restore hooks to support suspend and resume
  2026-05-26 19:28 ` [PATCH v9 1/3] drm/virtio: Freeze and restore hooks to support suspend and resume dongwon.kim
@ 2026-05-27  4:17   ` Claude Code Review Bot
  0 siblings, 0 replies; 9+ messages in thread
From: Claude Code Review Bot @ 2026-05-27  4:17 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

**Overall:** Clean extraction of `virtio_gpu_find_vqs()` helper and a well-structured `virtio_gpu_wait_queue()`. The freeze/restore flow is correct.

**Issue 1 — Missing error recovery in freeze (medium severity):**

If `virtio_gpu_wait_queue` times out after `drm_mode_config_helper_suspend()` has already succeeded, the function returns an error but leaves the DRM subsystem in a suspended state:

```c
error = drm_mode_config_helper_suspend(dev);
if (error) {
    DRM_ERROR("suspend error %d\n", error);
    return error;
}

flush_work(&vgdev->obj_free_work);
// ...

error = virtio_gpu_wait_queue(&vgdev->ctrlq, vgdev->ctrlq.vq->num_max);

if (error) {
    DRM_ERROR("suspend error %d\n", error);
    return error;  // DRM still suspended — no recovery
}
```

The error paths after the successful suspend should call `drm_mode_config_helper_resume(dev)` before returning.

**Issue 2 — Redundant min() in wait_queue call site:**

The caller passes `vgdev->ctrlq.vq->num_max` and `virtio_gpu_wait_queue` caps at `vgvq->vq->num_max`. This is harmless (the helper is designed for general use), just noting it.

**Nit — Extra blank lines in freeze between call and error check:**

```c
error = virtio_gpu_wait_queue(&vgdev->ctrlq, vgdev->ctrlq.vq->num_max);

if (error) {
```

Typical kernel style omits the blank line between a function call and its error check. Minor.

---

---
Generated by Claude Code Patch Reviewer

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

* Claude review: drm/virtio: Add support for saving and restoring virtio_gpu_objects
  2026-05-26 19:28 ` [PATCH v9 2/3] drm/virtio: Add support for saving and restoring virtio_gpu_objects dongwon.kim
@ 2026-05-27  4:17   ` Claude Code Review Bot
  0 siblings, 0 replies; 9+ messages in thread
From: Claude Code Review Bot @ 2026-05-27  4:17 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

**Overall:** The restore-list tracking approach is sensible. Objects are tracked at creation and removed before free, with proper mutex protection. The restore path correctly differentiates between blob, non-blob, and imported dmabuf objects.

**Issue 1 — VRAM objects silently excluded from restoration:**

`virtio_gpu_vram_create()` initializes the restore_node but never adds the object to the restore list:

```c
INIT_LIST_HEAD(&vram->base.restore_node);
// ... no call to virtio_gpu_add_object_to_restore_list()
```

Yet `virtio_gpu_vram_free()` calls `virtio_gpu_remove_from_restore_list(bo)`. The `list_del_init` on a self-referencing node is safe, so this won't crash, but VRAM objects won't survive hibernation. If this is intentional (VRAM objects are typically host-visible blobs that may require special handling), it should be documented in the commit message or with a comment.

**Issue 2 — Uninitialized `ents` on non-blob/non-attached path (not a bug):**

In `virtio_gpu_object_restore_all()`:

```c
if (bo->params.blob || bo->attached) {
    ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents);
    if (ret)
        break;
}

if (bo->params.blob) {
    virtio_gpu_cmd_resource_create_blob(vgdev, bo, &bo->params,
                                        ents, nents);
} else {
    virtio_gpu_cmd_create_resource(vgdev, bo, &bo->params,
                                   NULL, NULL);
    if (bo->attached) {
        bo->attached = false;
        virtio_gpu_object_attach(vgdev, bo, ents, nents);
    }
}
```

If `!bo->params.blob && !bo->attached`, `ents`/`nents` are never initialized, but they're also never used — the else-branch calls `virtio_gpu_cmd_create_resource` with `NULL, NULL` and skips attach. This is correct but could confuse a reader. An explicit `ents = NULL; nents = 0;` initialization at declaration would help.

**Issue 3 — virtgpu_dma_buf_obj_resubmit always uses DMA addresses:**

The resubmit path unconditionally uses `sg_dma_address`/`sg_dma_len`:

```c
for_each_sgtable_dma_sg(bo->sgt, sl, i) {
    ents[i].addr = cpu_to_le64(sg_dma_address(sl));
    ents[i].length = cpu_to_le32(sg_dma_len(sl));
```

This is correct for imported dmabufs (which always come through `dma_buf_map_attachment` and use DMA-mapped addresses), but differs from `virtio_gpu_object_shmem_init` which has a `use_dma_api` split for non-imported objects. Just confirming this is intentional — and it is, since imported objects always go through the DMA API path.

**Nit — Extra blank line after `virtgpu_dma_buf_obj_resubmit()`:**

```c
	return 0;
}


static const struct drm_gem_object_funcs virtgpu_gem_dma_buf_funcs = {
```

Double blank line should be single.

---

---
Generated by Claude Code Patch Reviewer

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

* Claude review: drm/virtio: Add PM notifier to restore objects after hibernation
  2026-05-26 19:28 ` [PATCH v9 3/3] drm/virtio: Add PM notifier to restore objects after hibernation dongwon.kim
@ 2026-05-27  4:17   ` Claude Code Review Bot
  0 siblings, 0 replies; 9+ messages in thread
From: Claude Code Review Bot @ 2026-05-27  4:17 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

**Overall:** The notifier approach correctly gates the restore logic behind PM_HIBERNATION_PREPARE. The VIRGL 3D check is a reasonable guard since virgl resources can't be restored.

**Issue 1 — `no_cb` parameter should be `bool`, not `int`:**

```c
void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
                                   struct virtio_gpu_object *bo,
                                   int no_cb)
```

All callers pass `true` or `false`. Using `bool` would be more idiomatic and self-documenting.

**Issue 2 — Unnecessary `#include <linux/pm_runtime.h>`:**

```c
#include <linux/suspend.h>
#include <linux/pm_runtime.h>
```

Nothing from `pm_runtime.h` is used. Only `<linux/suspend.h>` is needed for `register_pm_notifier()` and `PM_HIBERNATION_PREPARE`.

**Issue 3 — `virtio_gpu_notify` called per-object inside the loop:**

In `virtio_gpu_object_unref_all()`:

```c
list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list,
                         restore_node)
    if (bo->created) {
        virtio_gpu_cmd_unref_resource(vgdev, bo, true);
        virtio_gpu_notify(vgdev);
    }
```

The `virtio_gpu_notify` kicks the virtqueue. It could be called once after the loop instead of per-object. Not a correctness issue since this is the hibernation path, but it's cleaner.

**Issue 4 — Missing braces around multi-statement loop body:**

```c
list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list,
                         restore_node)
    if (bo->created) {
        virtio_gpu_cmd_unref_resource(vgdev, bo, true);
        virtio_gpu_notify(vgdev);
    }
```

Kernel coding style requires braces when the loop body is more than a simple statement. The `if` block should be wrapped:

```c
list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list,
                         restore_node) {
    if (bo->created) {
        virtio_gpu_cmd_unref_resource(vgdev, bo, true);
        virtio_gpu_notify(vgdev);
    }
}
```

**Issue 5 — `hibernation` flag not cleared on cancelled hibernation:**

If PM_HIBERNATION_PREPARE fires but hibernation is aborted before freeze is called, the `hibernation` flag remains `true` indefinitely. It's only cleared in `virtgpu_restore()`. While this is practically harmless (since `.freeze`/`.restore` are only called during S4 and the next S4 attempt would set the flag again anyway), handling `PM_POST_HIBERNATION` to clear the flag would be cleaner:

```c
if (mode == PM_POST_HIBERNATION)
    vgdev->hibernation = false;
```

**Issue 6 — Error recovery in freeze with hibernation (same as patch 1):**

Patch 3 adds `virtio_gpu_object_unref_all()` before flush_work:

```c
if (vgdev->hibernation)
    virtio_gpu_object_unref_all(vgdev);

flush_work(&vgdev->obj_free_work);
```

If the subsequent `wait_queue` calls fail, the objects have already been unreffed on the host side but the error path doesn't attempt to re-create them. This could leave the driver in an inconsistent state where guest objects exist but host resources have been destroyed. Combined with the missing `drm_mode_config_helper_resume` from patch 1, a timeout during hibernation freeze would leave the driver in a broken state.

---
Generated by Claude Code Patch Reviewer

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

end of thread, other threads:[~2026-05-27  4:17 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-26 19:28 [PATCH v9 0/3] Virtio-GPU S4 support dongwon.kim
2026-05-26 19:28 ` [PATCH v9 1/3] drm/virtio: Freeze and restore hooks to support suspend and resume dongwon.kim
2026-05-27  4:17   ` Claude review: " Claude Code Review Bot
2026-05-26 19:28 ` [PATCH v9 2/3] drm/virtio: Add support for saving and restoring virtio_gpu_objects dongwon.kim
2026-05-27  4:17   ` Claude review: " Claude Code Review Bot
2026-05-26 19:28 ` [PATCH v9 3/3] drm/virtio: Add PM notifier to restore objects after hibernation dongwon.kim
2026-05-27  4:17   ` Claude review: " Claude Code Review Bot
2026-05-27  4:17 ` Claude review: Virtio-GPU S4 support Claude Code Review Bot
  -- strict thread matches above, loose matches on Subject: below --
2026-04-29 20:47 [PATCH v8 0/3] Virtio-GPU S4 (hibernation) support dongwon.kim
2026-04-29 20:47 ` [PATCH v8 2/3] drm/virtio: Add support for saving and restoring virtio_gpu_objects dongwon.kim
2026-05-05  1:00   ` Claude review: " Claude Code Review Bot

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