1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2017 The Linux Foundation. All rights reserved. 3 */ 4 5 #include <linux/kref.h> 6 #include <linux/uaccess.h> 7 8 #include "msm_gpu.h" 9 10 void msm_submitqueue_destroy(struct kref *kref) 11 { 12 struct msm_gpu_submitqueue *queue = container_of(kref, 13 struct msm_gpu_submitqueue, ref); 14 15 idr_destroy(&queue->fence_idr); 16 17 msm_file_private_put(queue->ctx); 18 19 kfree(queue); 20 } 21 22 struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, 23 u32 id) 24 { 25 struct msm_gpu_submitqueue *entry; 26 27 if (!ctx) 28 return NULL; 29 30 read_lock(&ctx->queuelock); 31 32 list_for_each_entry(entry, &ctx->submitqueues, node) { 33 if (entry->id == id) { 34 kref_get(&entry->ref); 35 read_unlock(&ctx->queuelock); 36 37 return entry; 38 } 39 } 40 41 read_unlock(&ctx->queuelock); 42 return NULL; 43 } 44 45 void msm_submitqueue_close(struct msm_file_private *ctx) 46 { 47 struct msm_gpu_submitqueue *entry, *tmp; 48 49 if (!ctx) 50 return; 51 52 /* 53 * No lock needed in close and there won't 54 * be any more user ioctls coming our way 55 */ 56 list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node) { 57 list_del(&entry->node); 58 msm_submitqueue_put(entry); 59 } 60 } 61 62 int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx, 63 u32 prio, u32 flags, u32 *id) 64 { 65 struct msm_drm_private *priv = drm->dev_private; 66 struct msm_gpu_submitqueue *queue; 67 68 if (!ctx) 69 return -ENODEV; 70 71 if (!priv->gpu) 72 return -ENODEV; 73 74 if (prio >= priv->gpu->nr_rings) 75 return -EINVAL; 76 77 queue = kzalloc(sizeof(*queue), GFP_KERNEL); 78 79 if (!queue) 80 return -ENOMEM; 81 82 kref_init(&queue->ref); 83 queue->flags = flags; 84 queue->prio = prio; 85 86 write_lock(&ctx->queuelock); 87 88 queue->ctx = msm_file_private_get(ctx); 89 queue->id = ctx->queueid++; 90 91 if (id) 92 *id = queue->id; 93 94 idr_init(&queue->fence_idr); 95 mutex_init(&queue->lock); 96 97 list_add_tail(&queue->node, &ctx->submitqueues); 98 99 write_unlock(&ctx->queuelock); 100 101 return 0; 102 } 103 104 /* 105 * Create the default submit-queue (id==0), used for backwards compatibility 106 * for userspace that pre-dates the introduction of submitqueues. 107 */ 108 int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx) 109 { 110 struct msm_drm_private *priv = drm->dev_private; 111 int default_prio; 112 113 if (!priv->gpu) 114 return -ENODEV; 115 116 /* 117 * Select priority 2 as the "default priority" unless nr_rings is less 118 * than 2 and then pick the lowest priority 119 */ 120 default_prio = clamp_t(uint32_t, 2, 0, priv->gpu->nr_rings - 1); 121 122 INIT_LIST_HEAD(&ctx->submitqueues); 123 124 rwlock_init(&ctx->queuelock); 125 126 return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL); 127 } 128 129 static int msm_submitqueue_query_faults(struct msm_gpu_submitqueue *queue, 130 struct drm_msm_submitqueue_query *args) 131 { 132 size_t size = min_t(size_t, args->len, sizeof(queue->faults)); 133 int ret; 134 135 /* If a zero length was passed in, return the data size we expect */ 136 if (!args->len) { 137 args->len = sizeof(queue->faults); 138 return 0; 139 } 140 141 /* Set the length to the actual size of the data */ 142 args->len = size; 143 144 ret = copy_to_user(u64_to_user_ptr(args->data), &queue->faults, size); 145 146 return ret ? -EFAULT : 0; 147 } 148 149 int msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx, 150 struct drm_msm_submitqueue_query *args) 151 { 152 struct msm_gpu_submitqueue *queue; 153 int ret = -EINVAL; 154 155 if (args->pad) 156 return -EINVAL; 157 158 queue = msm_submitqueue_get(ctx, args->id); 159 if (!queue) 160 return -ENOENT; 161 162 if (args->param == MSM_SUBMITQUEUE_PARAM_FAULTS) 163 ret = msm_submitqueue_query_faults(queue, args); 164 165 msm_submitqueue_put(queue); 166 167 return ret; 168 } 169 170 int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id) 171 { 172 struct msm_gpu_submitqueue *entry; 173 174 if (!ctx) 175 return 0; 176 177 /* 178 * id 0 is the "default" queue and can't be destroyed 179 * by the user 180 */ 181 if (!id) 182 return -ENOENT; 183 184 write_lock(&ctx->queuelock); 185 186 list_for_each_entry(entry, &ctx->submitqueues, node) { 187 if (entry->id == id) { 188 list_del(&entry->node); 189 write_unlock(&ctx->queuelock); 190 191 msm_submitqueue_put(entry); 192 return 0; 193 } 194 } 195 196 write_unlock(&ctx->queuelock); 197 return -ENOENT; 198 } 199 200