From 1938d1ae32fefa172288e3a9245ae126640c5c10 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 26 Sep 2018 09:00:28 -0700 Subject: drm/virtio: Replace IDRs with IDAs These IDRs were only being used to allocate unique numbers, not to look up pointers, so they can use the more space-efficient IDA instead. Signed-off-by: Matthew Wilcox Link: http://patchwork.freedesktop.org/patch/msgid/20180926160031.15721-2-willy@infradead.org [ kraxel: resolve conflict ] Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_kms.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/virtio/virtgpu_kms.c') diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 65060c08522d..e2604fe1b4ae 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -55,21 +55,13 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) static void virtio_gpu_ctx_id_get(struct virtio_gpu_device *vgdev, uint32_t *resid) { - int handle; - - idr_preload(GFP_KERNEL); - spin_lock(&vgdev->ctx_id_idr_lock); - handle = idr_alloc(&vgdev->ctx_id_idr, NULL, 1, 0, 0); - spin_unlock(&vgdev->ctx_id_idr_lock); - idr_preload_end(); + int handle = ida_alloc_min(&vgdev->ctx_id_ida, 1, GFP_KERNEL); *resid = handle; } static void virtio_gpu_ctx_id_put(struct virtio_gpu_device *vgdev, uint32_t id) { - spin_lock(&vgdev->ctx_id_idr_lock); - idr_remove(&vgdev->ctx_id_idr, id); - spin_unlock(&vgdev->ctx_id_idr_lock); + ida_free(&vgdev->ctx_id_ida, id); } static void virtio_gpu_context_create(struct virtio_gpu_device *vgdev, @@ -151,10 +143,8 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) vgdev->dev = dev->dev; spin_lock_init(&vgdev->display_info_lock); - spin_lock_init(&vgdev->ctx_id_idr_lock); - idr_init(&vgdev->ctx_id_idr); - spin_lock_init(&vgdev->resource_idr_lock); - idr_init(&vgdev->resource_idr); + ida_init(&vgdev->ctx_id_ida); + ida_init(&vgdev->resource_ida); init_waitqueue_head(&vgdev->resp_wq); virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func); virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func); -- cgit v1.2.3-59-g8ed1b From 6a37c49a94a95d4cd8ce4e7466f3bbb17e80ca64 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 26 Sep 2018 09:00:29 -0700 Subject: drm/virtio: Handle context ID allocation errors It is possible to run out of memory while allocating IDs. The current code would create a context with an invalid ID; change it to return -ENOMEM to userspace. Signed-off-by: Matthew Wilcox Link: http://patchwork.freedesktop.org/patch/msgid/20180926160031.15721-3-willy@infradead.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_kms.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/virtio/virtgpu_kms.c') diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index e2604fe1b4ae..bf609dcae224 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -52,31 +52,22 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) events_clear, &events_clear); } -static void virtio_gpu_ctx_id_get(struct virtio_gpu_device *vgdev, - uint32_t *resid) +static int virtio_gpu_context_create(struct virtio_gpu_device *vgdev, + uint32_t nlen, const char *name) { int handle = ida_alloc_min(&vgdev->ctx_id_ida, 1, GFP_KERNEL); - *resid = handle; -} -static void virtio_gpu_ctx_id_put(struct virtio_gpu_device *vgdev, uint32_t id) -{ - ida_free(&vgdev->ctx_id_ida, id); -} - -static void virtio_gpu_context_create(struct virtio_gpu_device *vgdev, - uint32_t nlen, const char *name, - uint32_t *ctx_id) -{ - virtio_gpu_ctx_id_get(vgdev, ctx_id); - virtio_gpu_cmd_context_create(vgdev, *ctx_id, nlen, name); + if (handle < 0) + return handle; + virtio_gpu_cmd_context_create(vgdev, handle, nlen, name); + return handle; } static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev, uint32_t ctx_id) { virtio_gpu_cmd_context_destroy(vgdev, ctx_id); - virtio_gpu_ctx_id_put(vgdev, ctx_id); + ida_free(&vgdev->ctx_id_ida, ctx_id); } static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, @@ -261,7 +252,7 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) { struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_fpriv *vfpriv; - uint32_t id; + int id; char dbgname[TASK_COMM_LEN]; /* can't create contexts without 3d renderer */ @@ -274,7 +265,9 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) return -ENOMEM; get_task_comm(dbgname, current); - virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname, &id); + id = virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname); + if (id < 0) + return id; vfpriv->ctx_id = id; file->driver_priv = vfpriv; -- cgit v1.2.3-59-g8ed1b From 2ae7f165c0b022a8ed1732f8ab2c11b2c173146e Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 30 Oct 2018 09:53:52 -0700 Subject: drm/virtio: Use IDAs more efficiently 0-based IDAs are more efficient than any other base. Convert the 1-based IDAs to be 0-based. Signed-off-by: Matthew Wilcox Link: http://patchwork.freedesktop.org/patch/msgid/20181030165352.13065-2-willy@infradead.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_kms.c | 5 +++-- drivers/gpu/drm/virtio/virtgpu_object.c | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/virtio/virtgpu_kms.c') diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index bf609dcae224..8118f10fde4a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -55,10 +55,11 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) static int virtio_gpu_context_create(struct virtio_gpu_device *vgdev, uint32_t nlen, const char *name) { - int handle = ida_alloc_min(&vgdev->ctx_id_ida, 1, GFP_KERNEL); + int handle = ida_alloc(&vgdev->ctx_id_ida, GFP_KERNEL); if (handle < 0) return handle; + handle += 1; virtio_gpu_cmd_context_create(vgdev, handle, nlen, name); return handle; } @@ -67,7 +68,7 @@ static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev, uint32_t ctx_id) { virtio_gpu_cmd_context_destroy(vgdev, ctx_id); - ida_free(&vgdev->ctx_id_ida, ctx_id); + ida_free(&vgdev->ctx_id_ida, ctx_id - 1); } static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 5ac42dded217..f39a183d59c2 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -28,18 +28,18 @@ static int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, uint32_t *resid) { - int handle = ida_alloc_min(&vgdev->resource_ida, 1, GFP_KERNEL); + int handle = ida_alloc(&vgdev->resource_ida, GFP_KERNEL); if (handle < 0) return handle; - *resid = handle; + *resid = handle + 1; return 0; } static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id) { - ida_free(&vgdev->resource_ida, id); + ida_free(&vgdev->resource_ida, id - 1); } static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) -- cgit v1.2.3-59-g8ed1b From 040b595a91b21b61d6e0d388394b79c85f614e7c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 7 Nov 2018 20:31:22 +0000 Subject: drm/virtio: fix memory leak of vfpriv on error return path The allocation for vfpriv is being leaked on an error return path, fix this by kfree'ing it before returning. Detected by CoverityScan, CID#1475380 ("Resource Leak") Fixes: 6a37c49a94a9 ("drm/virtio: Handle context ID allocation errors") Signed-off-by: Colin Ian King Link: http://patchwork.freedesktop.org/patch/msgid/20181107203122.6861-1-colin.king@canonical.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_kms.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/virtio/virtgpu_kms.c') diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 8118f10fde4a..691b842d5f3a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -267,8 +267,10 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) get_task_comm(dbgname, current); id = virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname); - if (id < 0) + if (id < 0) { + kfree(vfpriv); return id; + } vfpriv->ctx_id = id; file->driver_priv = vfpriv; -- cgit v1.2.3-59-g8ed1b From b4b01b4995fb15b55a2d067eb405917f5ab32709 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Oct 2018 07:32:06 +0100 Subject: drm/virtio: add edid support linux guest driver implementation of the VIRTIO_GPU_F_EDID feature. Signed-off-by: Gerd Hoffmann Acked-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20181030063206.19528-3-kraxel@redhat.com --- drivers/gpu/drm/virtio/virtgpu_display.c | 12 ++++++ drivers/gpu/drm/virtio/virtgpu_drv.c | 1 + drivers/gpu/drm/virtio/virtgpu_drv.h | 3 ++ drivers/gpu/drm/virtio/virtgpu_kms.c | 8 ++++ drivers/gpu/drm/virtio/virtgpu_vq.c | 67 ++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+) (limited to 'drivers/gpu/drm/virtio/virtgpu_kms.c') diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 8f8fed471e34..b5580b11a063 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -169,6 +169,12 @@ static int virtio_gpu_conn_get_modes(struct drm_connector *connector) struct drm_display_mode *mode = NULL; int count, width, height; + if (output->edid) { + count = drm_add_edid_modes(connector, output->edid); + if (count) + return count; + } + width = le32_to_cpu(output->info.r.width); height = le32_to_cpu(output->info.r.height); count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); @@ -287,6 +293,8 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index) drm_connector_init(dev, connector, &virtio_gpu_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs); + if (vgdev->has_edid) + drm_connector_attach_edid_property(connector); drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs, DRM_MODE_ENCODER_VIRTUAL, NULL); @@ -378,6 +386,10 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev) { + int i; + + for (i = 0 ; i < vgdev->num_scanouts; ++i) + kfree(vgdev->outputs[i].edid); virtio_gpu_fbdev_fini(vgdev); drm_mode_config_cleanup(vgdev->ddev); } diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index d9287c144fe5..f7f32a885af7 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -80,6 +80,7 @@ static unsigned int features[] = { */ VIRTIO_GPU_F_VIRGL, #endif + VIRTIO_GPU_F_EDID, }; static struct virtio_driver virtio_gpu_driver = { .feature_table = features, diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 4632bd7e1972..7bec6e36886b 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -115,6 +115,7 @@ struct virtio_gpu_output { struct drm_encoder enc; struct virtio_gpu_display_one info; struct virtio_gpu_update_cursor cursor; + struct edid *edid; int cur_x; int cur_y; bool enabled; @@ -204,6 +205,7 @@ struct virtio_gpu_device { struct ida ctx_id_ida; bool has_virgl_3d; + bool has_edid; struct work_struct config_changed_work; @@ -294,6 +296,7 @@ int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx); int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev, int idx, int version, struct virtio_gpu_drv_cap_cache **cache_p); +int virtio_gpu_cmd_get_edids(struct virtio_gpu_device *vgdev); void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id, uint32_t nlen, const char *name); void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev, diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 691b842d5f3a..3af6181c05a8 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -44,6 +44,8 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) virtio_cread(vgdev->vdev, struct virtio_gpu_config, events_read, &events_read); if (events_read & VIRTIO_GPU_EVENT_DISPLAY) { + if (vgdev->has_edid) + virtio_gpu_cmd_get_edids(vgdev); virtio_gpu_cmd_get_display_info(vgdev); drm_helper_hpd_irq_event(vgdev->ddev); events_clear |= VIRTIO_GPU_EVENT_DISPLAY; @@ -156,6 +158,10 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) #else DRM_INFO("virgl 3d acceleration not supported by guest\n"); #endif + if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_EDID)) { + vgdev->has_edid = true; + DRM_INFO("EDID support available.\n"); + } ret = virtio_find_vqs(vgdev->vdev, 2, vqs, callbacks, names, NULL); if (ret) { @@ -201,6 +207,8 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) if (num_capsets) virtio_gpu_get_capsets(vgdev, num_capsets); + if (vgdev->has_edid) + virtio_gpu_cmd_get_edids(vgdev); virtio_gpu_cmd_get_display_info(vgdev); wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending, 5 * HZ); diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 93f2c3a51ee8..2c6764f08f18 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -584,6 +584,45 @@ static void virtio_gpu_cmd_capset_cb(struct virtio_gpu_device *vgdev, wake_up(&vgdev->resp_wq); } +static int virtio_get_edid_block(void *data, u8 *buf, + unsigned int block, size_t len) +{ + struct virtio_gpu_resp_edid *resp = data; + size_t start = block * EDID_LENGTH; + + if (start + len > le32_to_cpu(resp->size)) + return -1; + memcpy(buf, resp->edid + start, len); + return 0; +} + +static void virtio_gpu_cmd_get_edid_cb(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + struct virtio_gpu_cmd_get_edid *cmd = + (struct virtio_gpu_cmd_get_edid *)vbuf->buf; + struct virtio_gpu_resp_edid *resp = + (struct virtio_gpu_resp_edid *)vbuf->resp_buf; + uint32_t scanout = le32_to_cpu(cmd->scanout); + struct virtio_gpu_output *output; + struct edid *new_edid, *old_edid; + + if (scanout >= vgdev->num_scanouts) + return; + output = vgdev->outputs + scanout; + + new_edid = drm_do_get_edid(&output->conn, virtio_get_edid_block, resp); + + spin_lock(&vgdev->display_info_lock); + old_edid = output->edid; + output->edid = new_edid; + drm_connector_update_edid_property(&output->conn, output->edid); + spin_unlock(&vgdev->display_info_lock); + + kfree(old_edid); + wake_up(&vgdev->resp_wq); +} + int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev) { struct virtio_gpu_ctrl_hdr *cmd_p; @@ -686,6 +725,34 @@ int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev, return 0; } +int virtio_gpu_cmd_get_edids(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_cmd_get_edid *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + void *resp_buf; + int scanout; + + if (WARN_ON(!vgdev->has_edid)) + return -EINVAL; + + for (scanout = 0; scanout < vgdev->num_scanouts; scanout++) { + resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_edid), + GFP_KERNEL); + if (!resp_buf) + return -ENOMEM; + + cmd_p = virtio_gpu_alloc_cmd_resp + (vgdev, &virtio_gpu_cmd_get_edid_cb, &vbuf, + sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_edid), + resp_buf); + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_EDID); + cmd_p->scanout = cpu_to_le32(scanout); + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); + } + + return 0; +} + void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id, uint32_t nlen, const char *name) { -- cgit v1.2.3-59-g8ed1b