2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00
linux/drivers/gpu/drm/msm/msm_submitqueue.c
Rob Clark 654e9c18df drm/msm: Fix crash on dev file close
If the device file was opened prior to fw being available (such as from
initrd before rootfs is mounted, when the initrd does not contain GPU
fw), that would cause a later crash when the dev file is closed due to
unitialized submitqueues list:

   CPU: 4 PID: 263 Comm: plymouthd Tainted: G        W         5.15.0-rc2-next-20210924 #2
   Hardware name: LENOVO 81JL/LNVNB161216, BIOS 9UCN33WW(V2.06) 06/ 4/2019
   pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
   pc : msm_submitqueue_close+0x30/0x190 [msm]
   lr : msm_postclose+0x54/0xf0 [msm]
   sp : ffff80001074bb80
   x29: ffff80001074bb80 x28: ffff03ad80c4db80 x27: ffff03ad80dc5ab0
   x26: 0000000000000000 x25: ffff03ad80dc5af8 x24: ffff03ad81e90800
   x23: 0000000000000000 x22: ffff03ad81e90800 x21: ffff03ad8b35e788
   x20: ffff03ad81e90878 x19: 0000000000000000 x18: 0000000000000000
   x17: 0000000000000000 x16: ffffda15f14f7940 x15: 0000000000000000
   x14: 0000000000000000 x13: 0000000000000001 x12: 0000000000000040
   x11: 0000000000000000 x10: 0000000000000000 x9 : ffffda15cd18ff88
   x8 : ffff03ad80c4db80 x7 : 0000000000000228 x6 : 0000000000000000
   x5 : 1793a4e807e636bd x4 : ffff03ad80c4db80 x3 : ffff03ad81e90878
   x2 : 0000000000000000 x1 : ffff03ad80c4db80 x0 : 0000000000000000
   Call trace:
    msm_submitqueue_close+0x30/0x190 [msm]
    msm_postclose+0x54/0xf0 [msm]
    drm_file_free.part.0+0x1cc/0x2e0 [drm]
    drm_close_helper.isra.0+0x74/0x84 [drm]
    drm_release+0x78/0x120 [drm]
    __fput+0x78/0x23c
    ____fput+0x1c/0x30
    task_work_run+0xcc/0x22c
    do_exit+0x304/0x9f4
    do_group_exit+0x44/0xb0
    __wake_up_parent+0x0/0x3c
    invoke_syscall+0x50/0x120
    el0_svc_common.constprop.0+0x4c/0xf4
    do_el0_svc+0x30/0x9c
    el0_svc+0x20/0x60
    el0t_64_sync_handler+0xe8/0xf0
    el0t_64_sync+0x1a0/0x1a4
   Code: aa0003f5 a90153f3 f8408eb3 aa1303e0 (f85e8674)
   ---[ end trace 39b2fa37509a2be2 ]---
   Fixing recursive fault but reboot is needed!

Fixes: 86c2a0f000 drm/msm: ("Small submitqueue creation cleanup")
Reported-by: Steev Klimaszewski <steev@kali.org>
Signed-off-by: Rob Clark <robdclark@chromium.org>
2021-10-01 13:10:20 -07:00

217 lines
4.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
*/
#include <linux/kref.h>
#include <linux/uaccess.h>
#include "msm_gpu.h"
void msm_submitqueue_destroy(struct kref *kref)
{
struct msm_gpu_submitqueue *queue = container_of(kref,
struct msm_gpu_submitqueue, ref);
idr_destroy(&queue->fence_idr);
drm_sched_entity_destroy(&queue->entity);
msm_file_private_put(queue->ctx);
kfree(queue);
}
struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
u32 id)
{
struct msm_gpu_submitqueue *entry;
if (!ctx)
return NULL;
read_lock(&ctx->queuelock);
list_for_each_entry(entry, &ctx->submitqueues, node) {
if (entry->id == id) {
kref_get(&entry->ref);
read_unlock(&ctx->queuelock);
return entry;
}
}
read_unlock(&ctx->queuelock);
return NULL;
}
void msm_submitqueue_close(struct msm_file_private *ctx)
{
struct msm_gpu_submitqueue *entry, *tmp;
if (!ctx)
return;
/*
* No lock needed in close and there won't
* be any more user ioctls coming our way
*/
list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node) {
list_del(&entry->node);
msm_submitqueue_put(entry);
}
}
int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
u32 prio, u32 flags, u32 *id)
{
struct msm_drm_private *priv = drm->dev_private;
struct msm_gpu_submitqueue *queue;
struct msm_ringbuffer *ring;
struct drm_gpu_scheduler *sched;
enum drm_sched_priority sched_prio;
unsigned ring_nr;
int ret;
if (!ctx)
return -ENODEV;
if (!priv->gpu)
return -ENODEV;
ret = msm_gpu_convert_priority(priv->gpu, prio, &ring_nr, &sched_prio);
if (ret)
return ret;
queue = kzalloc(sizeof(*queue), GFP_KERNEL);
if (!queue)
return -ENOMEM;
kref_init(&queue->ref);
queue->flags = flags;
queue->ring_nr = ring_nr;
ring = priv->gpu->rb[ring_nr];
sched = &ring->sched;
ret = drm_sched_entity_init(&queue->entity,
sched_prio, &sched, 1, NULL);
if (ret) {
kfree(queue);
return ret;
}
write_lock(&ctx->queuelock);
queue->ctx = msm_file_private_get(ctx);
queue->id = ctx->queueid++;
if (id)
*id = queue->id;
idr_init(&queue->fence_idr);
mutex_init(&queue->lock);
list_add_tail(&queue->node, &ctx->submitqueues);
write_unlock(&ctx->queuelock);
return 0;
}
/*
* Create the default submit-queue (id==0), used for backwards compatibility
* for userspace that pre-dates the introduction of submitqueues.
*/
int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx)
{
struct msm_drm_private *priv = drm->dev_private;
int default_prio, max_priority;
if (!priv->gpu)
return -ENODEV;
max_priority = (priv->gpu->nr_rings * NR_SCHED_PRIORITIES) - 1;
/*
* Pick a medium priority level as default. Lower numeric value is
* higher priority, so round-up to pick a priority that is not higher
* than the middle priority level.
*/
default_prio = DIV_ROUND_UP(max_priority, 2);
return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL);
}
static int msm_submitqueue_query_faults(struct msm_gpu_submitqueue *queue,
struct drm_msm_submitqueue_query *args)
{
size_t size = min_t(size_t, args->len, sizeof(queue->faults));
int ret;
/* If a zero length was passed in, return the data size we expect */
if (!args->len) {
args->len = sizeof(queue->faults);
return 0;
}
/* Set the length to the actual size of the data */
args->len = size;
ret = copy_to_user(u64_to_user_ptr(args->data), &queue->faults, size);
return ret ? -EFAULT : 0;
}
int msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx,
struct drm_msm_submitqueue_query *args)
{
struct msm_gpu_submitqueue *queue;
int ret = -EINVAL;
if (args->pad)
return -EINVAL;
queue = msm_submitqueue_get(ctx, args->id);
if (!queue)
return -ENOENT;
if (args->param == MSM_SUBMITQUEUE_PARAM_FAULTS)
ret = msm_submitqueue_query_faults(queue, args);
msm_submitqueue_put(queue);
return ret;
}
int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id)
{
struct msm_gpu_submitqueue *entry;
if (!ctx)
return 0;
/*
* id 0 is the "default" queue and can't be destroyed
* by the user
*/
if (!id)
return -ENOENT;
write_lock(&ctx->queuelock);
list_for_each_entry(entry, &ctx->submitqueues, node) {
if (entry->id == id) {
list_del(&entry->node);
write_unlock(&ctx->queuelock);
msm_submitqueue_put(entry);
return 0;
}
}
write_unlock(&ctx->queuelock);
return -ENOENT;
}