162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2017 The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/kref.h> 662306a36Sopenharmony_ci#include <linux/uaccess.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "msm_gpu.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ciint msm_file_private_set_sysprof(struct msm_file_private *ctx, 1162306a36Sopenharmony_ci struct msm_gpu *gpu, int sysprof) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci /* 1462306a36Sopenharmony_ci * Since pm_runtime and sysprof_active are both refcounts, we 1562306a36Sopenharmony_ci * call apply the new value first, and then unwind the previous 1662306a36Sopenharmony_ci * value 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci switch (sysprof) { 2062306a36Sopenharmony_ci default: 2162306a36Sopenharmony_ci return -EINVAL; 2262306a36Sopenharmony_ci case 2: 2362306a36Sopenharmony_ci pm_runtime_get_sync(&gpu->pdev->dev); 2462306a36Sopenharmony_ci fallthrough; 2562306a36Sopenharmony_ci case 1: 2662306a36Sopenharmony_ci refcount_inc(&gpu->sysprof_active); 2762306a36Sopenharmony_ci fallthrough; 2862306a36Sopenharmony_ci case 0: 2962306a36Sopenharmony_ci break; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* unwind old value: */ 3362306a36Sopenharmony_ci switch (ctx->sysprof) { 3462306a36Sopenharmony_ci case 2: 3562306a36Sopenharmony_ci pm_runtime_put_autosuspend(&gpu->pdev->dev); 3662306a36Sopenharmony_ci fallthrough; 3762306a36Sopenharmony_ci case 1: 3862306a36Sopenharmony_ci refcount_dec(&gpu->sysprof_active); 3962306a36Sopenharmony_ci fallthrough; 4062306a36Sopenharmony_ci case 0: 4162306a36Sopenharmony_ci break; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci ctx->sysprof = sysprof; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_civoid __msm_file_private_destroy(struct kref *kref) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct msm_file_private *ctx = container_of(kref, 5262306a36Sopenharmony_ci struct msm_file_private, ref); 5362306a36Sopenharmony_ci int i; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctx->entities); i++) { 5662306a36Sopenharmony_ci if (!ctx->entities[i]) 5762306a36Sopenharmony_ci continue; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci drm_sched_entity_destroy(ctx->entities[i]); 6062306a36Sopenharmony_ci kfree(ctx->entities[i]); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci msm_gem_address_space_put(ctx->aspace); 6462306a36Sopenharmony_ci kfree(ctx->comm); 6562306a36Sopenharmony_ci kfree(ctx->cmdline); 6662306a36Sopenharmony_ci kfree(ctx); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_civoid msm_submitqueue_destroy(struct kref *kref) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct msm_gpu_submitqueue *queue = container_of(kref, 7262306a36Sopenharmony_ci struct msm_gpu_submitqueue, ref); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci idr_destroy(&queue->fence_idr); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci msm_file_private_put(queue->ctx); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci kfree(queue); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, 8262306a36Sopenharmony_ci u32 id) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct msm_gpu_submitqueue *entry; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!ctx) 8762306a36Sopenharmony_ci return NULL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci read_lock(&ctx->queuelock); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci list_for_each_entry(entry, &ctx->submitqueues, node) { 9262306a36Sopenharmony_ci if (entry->id == id) { 9362306a36Sopenharmony_ci kref_get(&entry->ref); 9462306a36Sopenharmony_ci read_unlock(&ctx->queuelock); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return entry; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci read_unlock(&ctx->queuelock); 10162306a36Sopenharmony_ci return NULL; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_civoid msm_submitqueue_close(struct msm_file_private *ctx) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct msm_gpu_submitqueue *entry, *tmp; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (!ctx) 10962306a36Sopenharmony_ci return; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * No lock needed in close and there won't 11362306a36Sopenharmony_ci * be any more user ioctls coming our way 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node) { 11662306a36Sopenharmony_ci list_del(&entry->node); 11762306a36Sopenharmony_ci msm_submitqueue_put(entry); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic struct drm_sched_entity * 12262306a36Sopenharmony_ciget_sched_entity(struct msm_file_private *ctx, struct msm_ringbuffer *ring, 12362306a36Sopenharmony_ci unsigned ring_nr, enum drm_sched_priority sched_prio) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci static DEFINE_MUTEX(entity_lock); 12662306a36Sopenharmony_ci unsigned idx = (ring_nr * NR_SCHED_PRIORITIES) + sched_prio; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* We should have already validated that the requested priority is 12962306a36Sopenharmony_ci * valid by the time we get here. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if (WARN_ON(idx >= ARRAY_SIZE(ctx->entities))) 13262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci mutex_lock(&entity_lock); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!ctx->entities[idx]) { 13762306a36Sopenharmony_ci struct drm_sched_entity *entity; 13862306a36Sopenharmony_ci struct drm_gpu_scheduler *sched = &ring->sched; 13962306a36Sopenharmony_ci int ret; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci entity = kzalloc(sizeof(*ctx->entities[idx]), GFP_KERNEL); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = drm_sched_entity_init(entity, sched_prio, &sched, 1, NULL); 14462306a36Sopenharmony_ci if (ret) { 14562306a36Sopenharmony_ci mutex_unlock(&entity_lock); 14662306a36Sopenharmony_ci kfree(entity); 14762306a36Sopenharmony_ci return ERR_PTR(ret); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ctx->entities[idx] = entity; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci mutex_unlock(&entity_lock); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return ctx->entities[idx]; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciint msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx, 15962306a36Sopenharmony_ci u32 prio, u32 flags, u32 *id) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct msm_drm_private *priv = drm->dev_private; 16262306a36Sopenharmony_ci struct msm_gpu_submitqueue *queue; 16362306a36Sopenharmony_ci enum drm_sched_priority sched_prio; 16462306a36Sopenharmony_ci unsigned ring_nr; 16562306a36Sopenharmony_ci int ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!ctx) 16862306a36Sopenharmony_ci return -ENODEV; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (!priv->gpu) 17162306a36Sopenharmony_ci return -ENODEV; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ret = msm_gpu_convert_priority(priv->gpu, prio, &ring_nr, &sched_prio); 17462306a36Sopenharmony_ci if (ret) 17562306a36Sopenharmony_ci return ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci queue = kzalloc(sizeof(*queue), GFP_KERNEL); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!queue) 18062306a36Sopenharmony_ci return -ENOMEM; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci kref_init(&queue->ref); 18362306a36Sopenharmony_ci queue->flags = flags; 18462306a36Sopenharmony_ci queue->ring_nr = ring_nr; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci queue->entity = get_sched_entity(ctx, priv->gpu->rb[ring_nr], 18762306a36Sopenharmony_ci ring_nr, sched_prio); 18862306a36Sopenharmony_ci if (IS_ERR(queue->entity)) { 18962306a36Sopenharmony_ci ret = PTR_ERR(queue->entity); 19062306a36Sopenharmony_ci kfree(queue); 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci write_lock(&ctx->queuelock); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci queue->ctx = msm_file_private_get(ctx); 19762306a36Sopenharmony_ci queue->id = ctx->queueid++; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (id) 20062306a36Sopenharmony_ci *id = queue->id; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci idr_init(&queue->fence_idr); 20362306a36Sopenharmony_ci spin_lock_init(&queue->idr_lock); 20462306a36Sopenharmony_ci mutex_init(&queue->lock); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci list_add_tail(&queue->node, &ctx->submitqueues); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci write_unlock(&ctx->queuelock); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * Create the default submit-queue (id==0), used for backwards compatibility 21562306a36Sopenharmony_ci * for userspace that pre-dates the introduction of submitqueues. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ciint msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct msm_drm_private *priv = drm->dev_private; 22062306a36Sopenharmony_ci int default_prio, max_priority; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!priv->gpu) 22362306a36Sopenharmony_ci return -ENODEV; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci max_priority = (priv->gpu->nr_rings * NR_SCHED_PRIORITIES) - 1; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * Pick a medium priority level as default. Lower numeric value is 22962306a36Sopenharmony_ci * higher priority, so round-up to pick a priority that is not higher 23062306a36Sopenharmony_ci * than the middle priority level. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci default_prio = DIV_ROUND_UP(max_priority, 2); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int msm_submitqueue_query_faults(struct msm_gpu_submitqueue *queue, 23862306a36Sopenharmony_ci struct drm_msm_submitqueue_query *args) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci size_t size = min_t(size_t, args->len, sizeof(queue->faults)); 24162306a36Sopenharmony_ci int ret; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* If a zero length was passed in, return the data size we expect */ 24462306a36Sopenharmony_ci if (!args->len) { 24562306a36Sopenharmony_ci args->len = sizeof(queue->faults); 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Set the length to the actual size of the data */ 25062306a36Sopenharmony_ci args->len = size; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = copy_to_user(u64_to_user_ptr(args->data), &queue->faults, size); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return ret ? -EFAULT : 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciint msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx, 25862306a36Sopenharmony_ci struct drm_msm_submitqueue_query *args) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct msm_gpu_submitqueue *queue; 26162306a36Sopenharmony_ci int ret = -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (args->pad) 26462306a36Sopenharmony_ci return -EINVAL; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci queue = msm_submitqueue_get(ctx, args->id); 26762306a36Sopenharmony_ci if (!queue) 26862306a36Sopenharmony_ci return -ENOENT; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (args->param == MSM_SUBMITQUEUE_PARAM_FAULTS) 27162306a36Sopenharmony_ci ret = msm_submitqueue_query_faults(queue, args); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci msm_submitqueue_put(queue); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return ret; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ciint msm_submitqueue_remove(struct msm_file_private *ctx, u32 id) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct msm_gpu_submitqueue *entry; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (!ctx) 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* 28662306a36Sopenharmony_ci * id 0 is the "default" queue and can't be destroyed 28762306a36Sopenharmony_ci * by the user 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci if (!id) 29062306a36Sopenharmony_ci return -ENOENT; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci write_lock(&ctx->queuelock); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci list_for_each_entry(entry, &ctx->submitqueues, node) { 29562306a36Sopenharmony_ci if (entry->id == id) { 29662306a36Sopenharmony_ci list_del(&entry->node); 29762306a36Sopenharmony_ci write_unlock(&ctx->queuelock); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci msm_submitqueue_put(entry); 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci write_unlock(&ctx->queuelock); 30562306a36Sopenharmony_ci return -ENOENT; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 308