summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/msm_submitqueue.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/msm_submitqueue.c')
-rw-r--r--drivers/gpu/drm/msm/msm_submitqueue.c266
1 files changed, 233 insertions, 33 deletions
diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c
index 5115f75b5b7f..d53dfad16bde 100644
--- a/drivers/gpu/drm/msm/msm_submitqueue.c
+++ b/drivers/gpu/drm/msm/msm_submitqueue.c
@@ -1,28 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/kref.h>
+#include <linux/uaccess.h>
+
#include "msm_gpu.h"
+int msm_context_set_sysprof(struct msm_context *ctx, struct msm_gpu *gpu, int sysprof)
+{
+ /*
+ * Since pm_runtime and sysprof_active are both refcounts, we
+ * call apply the new value first, and then unwind the previous
+ * value
+ */
+
+ switch (sysprof) {
+ default:
+ return UERR(EINVAL, gpu->dev, "Invalid sysprof: %d", sysprof);
+ case 2:
+ pm_runtime_get_sync(&gpu->pdev->dev);
+ fallthrough;
+ case 1:
+ refcount_inc(&gpu->sysprof_active);
+ fallthrough;
+ case 0:
+ break;
+ }
+
+ /* unwind old value: */
+ switch (ctx->sysprof) {
+ case 2:
+ pm_runtime_put_autosuspend(&gpu->pdev->dev);
+ fallthrough;
+ case 1:
+ refcount_dec(&gpu->sysprof_active);
+ fallthrough;
+ case 0:
+ break;
+ }
+
+ /* Some gpu families require additional setup for sysprof */
+ if (gpu->funcs->sysprof_setup)
+ gpu->funcs->sysprof_setup(gpu);
+
+ ctx->sysprof = sysprof;
+
+ return 0;
+}
+
+void __msm_context_destroy(struct kref *kref)
+{
+ struct msm_context *ctx = container_of(kref,
+ struct msm_context, ref);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctx->entities); i++) {
+ if (!ctx->entities[i])
+ continue;
+
+ drm_sched_entity_destroy(ctx->entities[i]);
+ kfree(ctx->entities[i]);
+ }
+
+ drm_gpuvm_put(ctx->vm);
+ kfree(ctx->comm);
+ kfree(ctx->cmdline);
+ kfree(ctx);
+}
+
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);
+
+ if (queue->entity == &queue->_vm_bind_entity[0])
+ drm_sched_entity_destroy(queue->entity);
+
+ msm_context_put(queue->ctx);
+
kfree(queue);
}
-struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
+struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_context *ctx,
u32 id)
{
struct msm_gpu_submitqueue *entry;
@@ -45,9 +107,9 @@ struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
return NULL;
}
-void msm_submitqueue_close(struct msm_file_private *ctx)
+void msm_submitqueue_close(struct msm_context *ctx)
{
- struct msm_gpu_submitqueue *entry, *tmp;
+ struct msm_gpu_submitqueue *queue, *tmp;
if (!ctx)
return;
@@ -56,20 +118,97 @@ void msm_submitqueue_close(struct msm_file_private *ctx)
* 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)
- msm_submitqueue_put(entry);
+ list_for_each_entry_safe(queue, tmp, &ctx->submitqueues, node) {
+ if (queue->entity == &queue->_vm_bind_entity[0])
+ drm_sched_entity_flush(queue->entity, MAX_WAIT_SCHED_ENTITY_Q_EMPTY);
+ list_del(&queue->node);
+ msm_submitqueue_put(queue);
+ }
+
+ if (!ctx->vm)
+ return;
+
+ msm_gem_vm_close(ctx->vm);
}
-int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
+static struct drm_sched_entity *
+get_sched_entity(struct msm_context *ctx, struct msm_ringbuffer *ring,
+ unsigned ring_nr, enum drm_sched_priority sched_prio)
+{
+ static DEFINE_MUTEX(entity_lock);
+ unsigned idx = (ring_nr * NR_SCHED_PRIORITIES) + sched_prio;
+
+ /* We should have already validated that the requested priority is
+ * valid by the time we get here.
+ */
+ if (WARN_ON(idx >= ARRAY_SIZE(ctx->entities)))
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&entity_lock);
+
+ if (!ctx->entities[idx]) {
+ struct drm_sched_entity *entity;
+ struct drm_gpu_scheduler *sched = &ring->sched;
+ int ret;
+
+ entity = kzalloc(sizeof(*ctx->entities[idx]), GFP_KERNEL);
+
+ ret = drm_sched_entity_init(entity, sched_prio, &sched, 1, NULL);
+ if (ret) {
+ mutex_unlock(&entity_lock);
+ kfree(entity);
+ return ERR_PTR(ret);
+ }
+
+ ctx->entities[idx] = entity;
+ }
+
+ mutex_unlock(&entity_lock);
+
+ return ctx->entities[idx];
+}
+
+int msm_submitqueue_create(struct drm_device *drm, struct msm_context *ctx,
u32 prio, u32 flags, u32 *id)
{
struct msm_drm_private *priv = drm->dev_private;
struct msm_gpu_submitqueue *queue;
+ enum drm_sched_priority sched_prio;
+ unsigned ring_nr;
+ int ret;
if (!ctx)
return -ENODEV;
- queue = kzalloc(sizeof(*queue), GFP_KERNEL);
+ if (!priv->gpu)
+ return -ENODEV;
+
+ if (flags & MSM_SUBMITQUEUE_VM_BIND) {
+ unsigned sz;
+
+ /* Not allowed for kernel managed VMs (ie. kernel allocs VA) */
+ if (!msm_context_is_vmbind(ctx))
+ return -EINVAL;
+
+ if (prio)
+ return -EINVAL;
+
+ sz = struct_size(queue, _vm_bind_entity, 1);
+ queue = kzalloc(sz, GFP_KERNEL);
+ } else {
+ extern int enable_preemption;
+ bool preemption_supported =
+ priv->gpu->nr_rings == 1 && enable_preemption != 0;
+
+ if (flags & MSM_SUBMITQUEUE_ALLOW_PREEMPT && preemption_supported)
+ return -EINVAL;
+
+ 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;
@@ -77,20 +216,38 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
kref_init(&queue->ref);
queue->flags = flags;
- if (priv->gpu) {
- if (prio >= priv->gpu->nr_rings)
- return -EINVAL;
+ if (flags & MSM_SUBMITQUEUE_VM_BIND) {
+ struct drm_gpu_scheduler *sched = &to_msm_vm(msm_context_vm(drm, ctx))->sched;
+
+ queue->entity = &queue->_vm_bind_entity[0];
+
+ drm_sched_entity_init(queue->entity, DRM_SCHED_PRIORITY_KERNEL,
+ &sched, 1, NULL);
+ } else {
+ queue->ring_nr = ring_nr;
+
+ queue->entity = get_sched_entity(ctx, priv->gpu->rb[ring_nr],
+ ring_nr, sched_prio);
+ }
- queue->prio = prio;
+ if (IS_ERR(queue->entity)) {
+ ret = PTR_ERR(queue->entity);
+ kfree(queue);
+ return ret;
}
write_lock(&ctx->queuelock);
+ queue->ctx = msm_context_get(ctx);
queue->id = ctx->queueid++;
if (id)
*id = queue->id;
+ idr_init(&queue->fence_idr);
+ spin_lock_init(&queue->idr_lock);
+ mutex_init(&queue->lock);
+
list_add_tail(&queue->node, &ctx->submitqueues);
write_unlock(&ctx->queuelock);
@@ -98,29 +255,72 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
return 0;
}
-int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx)
+/*
+ * 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_context *ctx)
{
struct msm_drm_private *priv = drm->dev_private;
- int default_prio;
+ int default_prio, max_priority;
- if (!ctx)
- return 0;
+ if (!priv->gpu)
+ return -ENODEV;
+
+ max_priority = (priv->gpu->nr_rings * NR_SCHED_PRIORITIES) - 1;
/*
- * Select priority 2 as the "default priority" unless nr_rings is less
- * than 2 and then pick the lowest pirority
+ * 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 = priv->gpu ?
- clamp_t(uint32_t, 2, 0, priv->gpu->nr_rings - 1) : 0;
+ default_prio = DIV_ROUND_UP(max_priority, 2);
- INIT_LIST_HEAD(&ctx->submitqueues);
+ return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL);
+}
- rwlock_init(&ctx->queuelock);
+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;
- return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL);
+ /* 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_context *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)
+int msm_submitqueue_remove(struct msm_context *ctx, u32 id)
{
struct msm_gpu_submitqueue *entry;