summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDavid Riley <davidriley@chromium.org>2019-06-05 16:44:22 -0700
committerGerd Hoffmann <kraxel@redhat.com>2019-06-06 09:44:40 +0200
commit5934ce998faf33277cc19522609577be835e80b1 (patch)
treec5fd01c9eca7b8521fd0c3c880e97dc2edc32204 /drivers/gpu
parent676a905b09d533ab3a30da753dba4f2daee97402 (diff)
drm/virtio: Fix cache entry creation race.
virtio_gpu_cmd_get_capset would check for the existence of an entry under lock. If it was not found, it would unlock and call virtio_gpu_cmd_get_capset to create a new entry. The new entry would be added it to the list without checking if it was added by another task during the period where the lock was not held resulting in duplicate entries. Compounding this issue, virtio_gpu_cmd_capset_cb would stop iterating after find the first matching entry. Multiple callbacks would modify the first entry, but any subsequent entries and their associated waiters would eventually timeout since they don't become valid, also wasting memory along the way. Signed-off-by: David Riley <davidriley@chromium.org> Link: http://patchwork.freedesktop.org/patch/msgid/20190605234423.11348-3-davidriley@chromium.org Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c22
1 files changed, 21 insertions, 1 deletions
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 2bac1cbc5ec8..5de38b2ee9ac 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -694,8 +694,11 @@ int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf;
int max_size;
struct virtio_gpu_drv_cap_cache *cache_ent;
+ struct virtio_gpu_drv_cap_cache *search_ent;
void *resp_buf;
+ *cache_p = NULL;
+
if (idx >= vgdev->num_capsets)
return -EINVAL;
@@ -726,9 +729,26 @@ int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
atomic_set(&cache_ent->is_valid, 0);
cache_ent->size = max_size;
spin_lock(&vgdev->display_info_lock);
- list_add_tail(&cache_ent->head, &vgdev->cap_cache);
+ /* Search while under lock in case it was added by another task. */
+ list_for_each_entry(search_ent, &vgdev->cap_cache, head) {
+ if (search_ent->id == vgdev->capsets[idx].id &&
+ search_ent->version == version) {
+ *cache_p = search_ent;
+ break;
+ }
+ }
+ if (!*cache_p)
+ list_add_tail(&cache_ent->head, &vgdev->cap_cache);
spin_unlock(&vgdev->display_info_lock);
+ if (*cache_p) {
+ /* Entry was found, so free everything that was just created. */
+ kfree(resp_buf);
+ kfree(cache_ent->caps_cache);
+ kfree(cache_ent);
+ return 0;
+ }
+
cmd_p = virtio_gpu_alloc_cmd_resp
(vgdev, &virtio_gpu_cmd_capset_cb, &vbuf, sizeof(*cmd_p),
sizeof(struct virtio_gpu_resp_capset) + max_size,