[PATCH] drm/virtio: fix the black screen error when waking

liweishi liweishi at kylinos.cn
Wed Sep 28 07:29:04 UTC 2022


Mainline: NA
Category: Bugfix
CVE: NA

When the system wakes up from sleeping, all virtio devices
will be reset. However, restting virtio gpu device will delete
the virtqueue and resources saved on the virtio gpu backend,
making it impossible for the virtio gpu driver to communicate
with the virtio gpu backend and causing a black screen error.
Rebuild the virtqueue and resources can avoid this problem.

Signed-off-by: liweishi<liweishi at kylinos.cn>

Suggested-by: Ming Xie<xieming at kylinos.cn>
---
 drivers/gpu/drm/virtio/virtgpu_drv.c    | 22 +++++++++++-
 drivers/gpu/drm/virtio/virtgpu_drv.h    | 12 +++++++
 drivers/gpu/drm/virtio/virtgpu_kms.c    | 47 +++++++++++++++++++++++++
 drivers/gpu/drm/virtio/virtgpu_object.c | 40 +++++++++++++++++++++
 4 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 0035affc3e59..910b146d7e52 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -129,6 +129,23 @@ static void virtio_gpu_config_changed(struct virtio_device *vdev)
 	schedule_work(&vgdev->config_changed_work);
 }
 
+#ifdef CONFIG_PM_SLEEP
+/*
+ * when the system wakes up from sleeping, all virtio devices
+ * will be reset. However, restting virtio gpu device will delete
+ * the virtqueue and resources saved on the virtio gpu backend,
+ * making it impossible for the virtio gpu driver to communicate
+ * with the virtio gpu backend and causing a black screen problem.
+ * rebuild the virtqueue and resources can avoid this problem.
+ */
+static int virtio_gpu_restore(struct virtio_device *vdev)
+{
+	struct drm_device *dev = vdev->priv;
+
+	return virtio_gpu_rebuild(dev);
+}
+#endif
+
 static struct virtio_device_id id_table[] = {
 	{ VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID },
 	{ 0 },
@@ -156,7 +173,10 @@ static struct virtio_driver virtio_gpu_driver = {
 	.id_table = id_table,
 	.probe = virtio_gpu_probe,
 	.remove = virtio_gpu_remove,
-	.config_changed = virtio_gpu_config_changed
+	.config_changed = virtio_gpu_config_changed,
+#ifdef CONFIG_PM_SLEEP
+	.restore = virtio_gpu_restore,
+#endif
 };
 
 module_virtio_driver(virtio_gpu_driver);
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 9b98470593b0..b652b3790bb1 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -212,6 +212,13 @@ struct virtio_gpu_drv_cap_cache {
 	atomic_t is_valid;
 };
 
+struct virtio_gpu_object_resource {
+	struct list_head head;
+	struct virtio_gpu_object *bo;
+	/* parameters used in resource creation */
+	struct virtio_gpu_object_params params;
+};
+
 struct virtio_gpu_device {
 	struct drm_device *ddev;
 
@@ -262,6 +269,8 @@ struct virtio_gpu_device {
 	spinlock_t resource_export_lock;
 	/* protects map state and host_visible_mm */
 	spinlock_t host_visible_lock;
+	/* stores resource creation information */
+	struct list_head res_list;
 };
 
 struct virtio_gpu_fpriv {
@@ -285,6 +294,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_rebuild(struct drm_device *dev);
 
 /* virtgpu_gem.c */
 int virtio_gpu_gem_object_open(struct drm_gem_object *obj,
@@ -456,6 +466,8 @@ 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_recreate_resource(struct virtio_gpu_device *vgdev,
+				    struct virtio_gpu_object_resource *vgor);
 /* 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 27b7f14dae89..72a6bb3e9502 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -149,6 +149,7 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
 	spin_lock_init(&vgdev->fence_drv.lock);
 	INIT_LIST_HEAD(&vgdev->fence_drv.fences);
 	INIT_LIST_HEAD(&vgdev->cap_cache);
+	INIT_LIST_HEAD(&vgdev->res_list);
 	INIT_WORK(&vgdev->config_changed_work,
 		  virtio_gpu_config_changed_work_func);
 
@@ -271,6 +272,15 @@ static void virtio_gpu_cleanup_cap_cache(struct virtio_gpu_device *vgdev)
 	}
 }
 
+static void virtio_gpu_cleanup_object_resources(struct virtio_gpu_device *vgdev)
+{
+	struct virtio_gpu_object_resource *vgor, *tmp;
+
+	list_for_each_entry_safe(vgor, tmp, &vgdev->res_list, head) {
+		kfree(vgor);
+	}
+}
+
 void virtio_gpu_deinit(struct drm_device *dev)
 {
 	struct virtio_gpu_device *vgdev = dev->dev_private;
@@ -293,11 +303,48 @@ void virtio_gpu_release(struct drm_device *dev)
 	virtio_gpu_modeset_fini(vgdev);
 	virtio_gpu_free_vbufs(vgdev);
 	virtio_gpu_cleanup_cap_cache(vgdev);
+	virtio_gpu_cleanup_object_resources(vgdev);
 
 	if (vgdev->has_host_visible)
 		drm_mm_takedown(&vgdev->host_visible_mm);
 }
 
+int virtio_gpu_rebuild(struct drm_device *dev)
+{
+	struct virtio_gpu_device *vgdev = dev->dev_private;
+	struct virtio_device *vdev = vgdev->vdev;
+
+	/* rebuild vgdev->ctrlq.vq and vgdev->cursorq.vq */
+	struct virtqueue *vqs[2];
+	int ret;
+
+	static vq_callback_t *callbacks[] = {
+		virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack
+	};
+	static const char * const names[] = { "control", "cursor" };
+
+	vdev->config->del_vqs(vdev);
+	ret = virtio_find_vqs(vdev, 2, vqs, callbacks, names, NULL);
+	if (ret)
+		return ret;
+
+	vgdev->ctrlq.vq = vqs[0];
+	vgdev->cursorq.vq = vqs[1];
+
+	/*
+	 * if resource_blob and virgl 3d are not used,
+	 * all resources should be recreated.
+	 */
+	if (!vgdev->has_resource_blob && !vgdev->has_virgl_3d) {
+		struct virtio_gpu_object_resource *vgor;
+
+		list_for_each_entry(vgor, &vgdev->res_list, head) {
+			virtio_gpu_recreate_resource(vgdev, vgor);
+		}
+	}
+	return 0;
+}
+
 int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file)
 {
 	struct virtio_gpu_device *vgdev = dev->dev_private;
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index 8d7728181de0..349a62a4992b 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -83,6 +83,30 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo)
 	}
 }
 
+static void virtio_gpu_object_resource_restore(struct virtio_gpu_device *vgdev,
+						struct virtio_gpu_object *bo,
+						struct virtio_gpu_object_params *params)
+{
+	struct virtio_gpu_object_resource *vgor;
+
+	vgor = kzalloc(sizeof(*vgor), GFP_KERNEL);
+	vgor->bo = bo;
+	memcpy(&vgor->params, params, sizeof(*params));
+	list_add_tail(&vgor->head, &vgdev->res_list);
+}
+
+static void virtio_gpu_object_resource_remove(struct virtio_gpu_device *vgdev,
+						struct virtio_gpu_object *bo)
+{
+	struct virtio_gpu_object_resource *vgor, *tmp;
+
+	list_for_each_entry_safe(vgor, tmp, &vgdev->res_list, head) {
+		if (vgor->bo->hw_res_handle == bo->hw_res_handle) {
+			list_del(&vgor->head);
+		}
+	}
+}
+
 static void virtio_gpu_free_object(struct drm_gem_object *obj)
 {
 	struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
@@ -92,6 +116,8 @@ static void virtio_gpu_free_object(struct drm_gem_object *obj)
 		virtio_gpu_cmd_unref_resource(vgdev, bo);
 		virtio_gpu_notify(vgdev);
 		/* completion handler calls virtio_gpu_cleanup_object() */
+
+		virtio_gpu_object_resource_remove(vgdev, bo);
 		return;
 	}
 	virtio_gpu_cleanup_object(bo);
@@ -232,6 +258,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
 		virtio_gpu_cmd_create_resource(vgdev, bo, params,
 					       objs, fence);
 		virtio_gpu_object_attach(vgdev, bo, ents, nents);
+		/* records information related to resource creation  */
+		virtio_gpu_object_resource_restore(vgdev, bo, params);
 	}
 
 	*bo_ptr = bo;
@@ -245,3 +273,15 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
 	drm_gem_shmem_free(shmem_obj);
 	return ret;
 }
+
+void virtio_gpu_recreate_resource(struct virtio_gpu_device *vgdev,
+			     struct virtio_gpu_object_resource *vgor)
+{
+	struct virtio_gpu_mem_entry *ents;
+	unsigned int nents;
+
+	if (virtio_gpu_object_shmem_init(vgdev, vgor->bo, &ents, &nents) != 0)
+		return;
+	virtio_gpu_cmd_create_resource(vgdev, vgor->bo, &vgor->params, NULL, NULL);
+	virtio_gpu_object_attach(vgdev, vgor->bo, ents, nents);
+}
-- 
2.25.1


No virus found
		Checked by Hillstone Network AntiVirus


More information about the dri-devel mailing list