diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index ed85a78632563e366bf9b6bde7baa7bc7a566ecb..82812d6868223af6e82ea7346876aef94f40d8d8 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -155,6 +155,21 @@ 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 }, @@ -181,7 +196,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 d4e610a44e12bdfc17a7eb450ba5b804333dc846..7597d73a82919e41373107fe242f413d0686e2de 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -201,6 +201,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 device *dev; struct drm_device *ddev; @@ -250,6 +257,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 { @@ -269,6 +278,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, @@ -438,6 +448,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 f3379059f324b84c59c1999013121a05e33763cd..322b79b48d1f512c0868b1d6491cf4939406e754 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -132,6 +132,7 @@ int virtio_gpu_init(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); @@ -249,6 +250,13 @@ 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; @@ -271,6 +279,7 @@ 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); @@ -279,6 +288,41 @@ void virtio_gpu_release(struct drm_device *dev) kfree(vgdev); } +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 f648b0e24447b3a4531579c77a69f6f7713b8fa2..db40f31042ea22289a6e28c0dd32eb284b22e05d 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -98,6 +98,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); @@ -107,6 +131,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); @@ -267,6 +293,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; @@ -280,3 +308,15 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, drm_gem_shmem_free_object(&shmem_obj->base); 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(0 != virtio_gpu_object_shmem_init(vgdev, vgor->bo, &ents, &nents)) + return; + virtio_gpu_cmd_create_resource(vgdev, vgor->bo, &vgor->params, NULL, NULL); + virtio_gpu_object_attach(vgdev, vgor->bo, ents, nents); +}