blob: 3b57b7359842184c79051f6550853190ff1e9f08 [file] [log] [blame]
Shrenuj Bansala419c792016-10-20 14:05:11 -07001/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/err.h>
15#include <linux/file.h>
16#include <linux/sched.h>
17#include <linux/slab.h>
18#include <linux/uaccess.h>
19
20#include <asm/current.h>
21
22#include "kgsl_sync.h"
23
Lynus Vazc031a9b2017-01-25 13:00:13 +053024static void kgsl_sync_timeline_signal(struct kgsl_sync_timeline *timeline,
Shrenuj Bansala419c792016-10-20 14:05:11 -070025 unsigned int timestamp);
26
Lynus Vazc031a9b2017-01-25 13:00:13 +053027static const struct fence_ops kgsl_sync_fence_ops;
28
29static struct kgsl_sync_fence *kgsl_sync_fence_create(
30 struct kgsl_context *context,
31 unsigned int timestamp)
Shrenuj Bansala419c792016-10-20 14:05:11 -070032{
Lynus Vazc031a9b2017-01-25 13:00:13 +053033 struct kgsl_sync_fence *kfence;
34 struct kgsl_sync_timeline *ktimeline = context->ktimeline;
35 unsigned long flags;
Shrenuj Bansala419c792016-10-20 14:05:11 -070036
Lynus Vazc031a9b2017-01-25 13:00:13 +053037 /* Get a refcount to the timeline. Put when released */
38 if (!kref_get_unless_zero(&ktimeline->kref))
39 return NULL;
Shrenuj Bansala419c792016-10-20 14:05:11 -070040
Lynus Vazc031a9b2017-01-25 13:00:13 +053041 kfence = kzalloc(sizeof(*kfence), GFP_KERNEL);
42 if (kfence == NULL) {
43 kgsl_sync_timeline_put(ktimeline);
44 KGSL_DRV_ERR(context->device, "Couldn't allocate fence\n");
45 return NULL;
Shrenuj Bansala419c792016-10-20 14:05:11 -070046 }
Lynus Vazc031a9b2017-01-25 13:00:13 +053047
48 kfence->parent = ktimeline;
49 kfence->context_id = context->id;
50 kfence->timestamp = timestamp;
51
52 fence_init(&kfence->fence, &kgsl_sync_fence_ops, &ktimeline->lock,
53 ktimeline->fence_context, timestamp);
54
55 kfence->sync_file = sync_file_create(&kfence->fence);
56
57 if (kfence->sync_file == NULL) {
58 kgsl_sync_timeline_put(ktimeline);
59 KGSL_DRV_ERR(context->device, "Create sync_file failed\n");
60 kfree(kfence);
61 return NULL;
62 }
63
64 /* Get a refcount to the fence. Put when signaled */
65 fence_get(&kfence->fence);
66
67 spin_lock_irqsave(&ktimeline->lock, flags);
68 list_add_tail(&kfence->child_list, &ktimeline->child_list_head);
69 spin_unlock_irqrestore(&ktimeline->lock, flags);
70
71 return kfence;
Shrenuj Bansala419c792016-10-20 14:05:11 -070072}
73
Lynus Vazc031a9b2017-01-25 13:00:13 +053074static void kgsl_sync_fence_release(struct fence *fence)
Shrenuj Bansala419c792016-10-20 14:05:11 -070075{
Lynus Vazc031a9b2017-01-25 13:00:13 +053076 struct kgsl_sync_fence *kfence = (struct kgsl_sync_fence *)fence;
77
78 kgsl_sync_timeline_put(kfence->parent);
79 kfree(kfence);
Shrenuj Bansala419c792016-10-20 14:05:11 -070080}
81
Lynus Vazc031a9b2017-01-25 13:00:13 +053082/* Called with ktimeline->lock held */
83bool kgsl_sync_fence_has_signaled(struct fence *fence)
Shrenuj Bansala419c792016-10-20 14:05:11 -070084{
Lynus Vazc031a9b2017-01-25 13:00:13 +053085 struct kgsl_sync_fence *kfence = (struct kgsl_sync_fence *)fence;
86 struct kgsl_sync_timeline *ktimeline = kfence->parent;
87 unsigned int ts = kfence->timestamp;
Shrenuj Bansala419c792016-10-20 14:05:11 -070088
Lynus Vazc031a9b2017-01-25 13:00:13 +053089 return (timestamp_cmp(ktimeline->last_timestamp, ts) >= 0);
Shrenuj Bansala419c792016-10-20 14:05:11 -070090}
91
Lynus Vazc031a9b2017-01-25 13:00:13 +053092bool kgsl_enable_signaling(struct fence *fence)
Shrenuj Bansala419c792016-10-20 14:05:11 -070093{
Lynus Vazc031a9b2017-01-25 13:00:13 +053094 return !kgsl_sync_fence_has_signaled(fence);
Shrenuj Bansala419c792016-10-20 14:05:11 -070095}
96
Lynus Vazc031a9b2017-01-25 13:00:13 +053097struct kgsl_sync_fence_event_priv {
Shrenuj Bansala419c792016-10-20 14:05:11 -070098 struct kgsl_context *context;
99 unsigned int timestamp;
100};
101
102/**
Lynus Vazc031a9b2017-01-25 13:00:13 +0530103 * kgsl_sync_fence_event_cb - Event callback for a fence timestamp event
Shrenuj Bansala419c792016-10-20 14:05:11 -0700104 * @device - The KGSL device that expired the timestamp
105 * @context- Pointer to the context that owns the event
106 * @priv: Private data for the callback
107 * @result - Result of the event (retired or canceled)
108 *
109 * Signal a fence following the expiration of a timestamp
110 */
111
Lynus Vazc031a9b2017-01-25 13:00:13 +0530112static void kgsl_sync_fence_event_cb(struct kgsl_device *device,
Shrenuj Bansala419c792016-10-20 14:05:11 -0700113 struct kgsl_event_group *group, void *priv, int result)
114{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530115 struct kgsl_sync_fence_event_priv *ev = priv;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700116
Lynus Vazc031a9b2017-01-25 13:00:13 +0530117 kgsl_sync_timeline_signal(ev->context->ktimeline, ev->timestamp);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700118 kgsl_context_put(ev->context);
119 kfree(ev);
120}
121
122static int _add_fence_event(struct kgsl_device *device,
123 struct kgsl_context *context, unsigned int timestamp)
124{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530125 struct kgsl_sync_fence_event_priv *event;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700126 int ret;
127
128 event = kmalloc(sizeof(*event), GFP_KERNEL);
129 if (event == NULL)
130 return -ENOMEM;
131
132 /*
133 * Increase the refcount for the context to keep it through the
134 * callback
135 */
136 if (!_kgsl_context_get(context)) {
137 kfree(event);
138 return -ENOENT;
139 }
140
141 event->context = context;
142 event->timestamp = timestamp;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700143
144 ret = kgsl_add_event(device, &context->events, timestamp,
Lynus Vazc031a9b2017-01-25 13:00:13 +0530145 kgsl_sync_fence_event_cb, event);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700146
147 if (ret) {
148 kgsl_context_put(context);
149 kfree(event);
150 }
151
152 return ret;
153}
154
155/**
156 * kgsl_add_fence_event - Create a new fence event
157 * @device - KGSL device to create the event on
158 * @timestamp - Timestamp to trigger the event
159 * @data - Return fence fd stored in struct kgsl_timestamp_event_fence
160 * @len - length of the fence event
161 * @owner - driver instance that owns this event
162 * @returns 0 on success or error code on error
163 *
164 * Create a fence and register an event to signal the fence when
165 * the timestamp expires
166 */
167
168int kgsl_add_fence_event(struct kgsl_device *device,
169 u32 context_id, u32 timestamp, void __user *data, int len,
170 struct kgsl_device_private *owner)
171{
172 struct kgsl_timestamp_event_fence priv;
173 struct kgsl_context *context;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530174 struct kgsl_sync_fence *kfence = NULL;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700175 int ret = -EINVAL;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700176 unsigned int cur;
177
178 priv.fence_fd = -1;
179
180 if (len != sizeof(priv))
181 return -EINVAL;
182
183 context = kgsl_context_get_owner(owner, context_id);
184
185 if (context == NULL)
186 return -EINVAL;
187
188 if (test_bit(KGSL_CONTEXT_PRIV_INVALID, &context->priv))
189 goto out;
190
Lynus Vazc031a9b2017-01-25 13:00:13 +0530191 kfence = kgsl_sync_fence_create(context, timestamp);
192 if (kfence == NULL) {
193 KGSL_DRV_CRIT_RATELIMIT(device,
194 "kgsl_sync_fence_create failed\n");
Shrenuj Bansala419c792016-10-20 14:05:11 -0700195 ret = -ENOMEM;
196 goto out;
197 }
198
199 priv.fence_fd = get_unused_fd_flags(0);
200 if (priv.fence_fd < 0) {
201 KGSL_DRV_CRIT_RATELIMIT(device,
202 "Unable to get a file descriptor: %d\n",
203 priv.fence_fd);
204 ret = priv.fence_fd;
205 goto out;
206 }
207
208 /*
209 * If the timestamp hasn't expired yet create an event to trigger it.
210 * Otherwise, just signal the fence - there is no reason to go through
211 * the effort of creating a fence we don't need.
212 */
213
214 kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED, &cur);
215
216 if (timestamp_cmp(cur, timestamp) >= 0) {
217 ret = 0;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530218 kgsl_sync_timeline_signal(context->ktimeline, cur);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700219 } else {
220 ret = _add_fence_event(device, context, timestamp);
221 if (ret)
222 goto out;
223 }
224
225 if (copy_to_user(data, &priv, sizeof(priv))) {
226 ret = -EFAULT;
227 goto out;
228 }
Lynus Vazc031a9b2017-01-25 13:00:13 +0530229 fd_install(priv.fence_fd, kfence->sync_file->file);
230
Shrenuj Bansala419c792016-10-20 14:05:11 -0700231out:
232 kgsl_context_put(context);
233 if (ret) {
234 if (priv.fence_fd >= 0)
235 put_unused_fd(priv.fence_fd);
236
Lynus Vazc031a9b2017-01-25 13:00:13 +0530237 if (kfence) {
238 /*
239 * Put the refcount of sync file. This will release
240 * kfence->fence as well.
241 */
242 fput(kfence->sync_file->file);
243 }
Shrenuj Bansala419c792016-10-20 14:05:11 -0700244 }
245 return ret;
246}
247
Lynus Vazc031a9b2017-01-25 13:00:13 +0530248static unsigned int kgsl_sync_fence_get_timestamp(
249 struct kgsl_sync_timeline *ktimeline,
250 enum kgsl_timestamp_type type)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700251{
252 unsigned int ret = 0;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700253
254 if (ktimeline->device == NULL)
255 return 0;
256
Lynus Vazc031a9b2017-01-25 13:00:13 +0530257 kgsl_readtimestamp(ktimeline->device, ktimeline->context, type, &ret);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700258
Shrenuj Bansala419c792016-10-20 14:05:11 -0700259 return ret;
260}
261
Lynus Vazc031a9b2017-01-25 13:00:13 +0530262static void kgsl_sync_timeline_value_str(struct fence *fence,
263 char *str, int size)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700264{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530265 struct kgsl_sync_fence *kfence = (struct kgsl_sync_fence *)fence;
266 struct kgsl_sync_timeline *ktimeline = kfence->parent;
267
268 unsigned int timestamp_retired;
269 unsigned int timestamp_queued;
270
271 if (!kref_get_unless_zero(&ktimeline->kref))
272 return;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700273
274 /*
275 * This callback can be called before the device and spinlock are
276 * initialized in struct kgsl_sync_timeline. kgsl_sync_get_timestamp()
277 * will check if device is NULL and return 0. Queued and retired
278 * timestamp of the context will be reported as 0, which is correct
279 * because the context and timeline are just getting initialized.
280 */
Lynus Vazc031a9b2017-01-25 13:00:13 +0530281 timestamp_retired = kgsl_sync_fence_get_timestamp(ktimeline,
282 KGSL_TIMESTAMP_RETIRED);
283 timestamp_queued = kgsl_sync_fence_get_timestamp(ktimeline,
284 KGSL_TIMESTAMP_QUEUED);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700285
286 snprintf(str, size, "%u queued:%u retired:%u",
287 ktimeline->last_timestamp,
288 timestamp_queued, timestamp_retired);
Lynus Vazc031a9b2017-01-25 13:00:13 +0530289
290 kgsl_sync_timeline_put(ktimeline);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700291}
292
Lynus Vazc031a9b2017-01-25 13:00:13 +0530293static void kgsl_sync_fence_value_str(struct fence *fence, char *str, int size)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700294{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530295 struct kgsl_sync_fence *kfence = (struct kgsl_sync_fence *)fence;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700296
Lynus Vazc031a9b2017-01-25 13:00:13 +0530297 snprintf(str, size, "%u", kfence->timestamp);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700298}
299
Lynus Vazc031a9b2017-01-25 13:00:13 +0530300static const char *kgsl_sync_fence_driver_name(struct fence *fence)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700301{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530302 return "kgsl-timeline";
Shrenuj Bansala419c792016-10-20 14:05:11 -0700303}
304
Lynus Vazc031a9b2017-01-25 13:00:13 +0530305static const char *kgsl_sync_timeline_name(struct fence *fence)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700306{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530307 struct kgsl_sync_fence *kfence = (struct kgsl_sync_fence *)fence;
308 struct kgsl_sync_timeline *ktimeline = kfence->parent;
309
310 return ktimeline->name;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700311}
Shrenuj Bansala419c792016-10-20 14:05:11 -0700312
313int kgsl_sync_timeline_create(struct kgsl_context *context)
314{
315 struct kgsl_sync_timeline *ktimeline;
316
317 /*
318 * Generate a name which includes the thread name, thread id, process
319 * name, process id, and context id. This makes it possible to
320 * identify the context of a timeline in the sync dump.
321 */
Lynus Vazc031a9b2017-01-25 13:00:13 +0530322 char ktimeline_name[sizeof(ktimeline->name)] = {};
323
324 /* Put context when timeline is released */
325 if (!_kgsl_context_get(context))
326 return -ENOENT;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700327
328 snprintf(ktimeline_name, sizeof(ktimeline_name),
329 "%s_%.15s(%d)-%.15s(%d)-%d",
330 context->device->name,
331 current->group_leader->comm, current->group_leader->pid,
332 current->comm, current->pid, context->id);
333
Lynus Vazc031a9b2017-01-25 13:00:13 +0530334 ktimeline = kzalloc(sizeof(*ktimeline), GFP_KERNEL);
335 if (ktimeline == NULL) {
336 kgsl_context_put(context);
337 return -ENOMEM;
338 }
Shrenuj Bansala419c792016-10-20 14:05:11 -0700339
Lynus Vazc031a9b2017-01-25 13:00:13 +0530340 kref_init(&ktimeline->kref);
341 strlcpy(ktimeline->name, ktimeline_name, KGSL_TIMELINE_NAME_LEN);
342 ktimeline->fence_context = fence_context_alloc(1);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700343 ktimeline->last_timestamp = 0;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530344 INIT_LIST_HEAD(&ktimeline->child_list_head);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700345 spin_lock_init(&ktimeline->lock);
Lynus Vazc031a9b2017-01-25 13:00:13 +0530346 ktimeline->device = context->device;
347 ktimeline->context = context;
348
349 context->ktimeline = ktimeline;
350
Shrenuj Bansala419c792016-10-20 14:05:11 -0700351 return 0;
352}
353
Lynus Vazc031a9b2017-01-25 13:00:13 +0530354static void kgsl_sync_timeline_signal(struct kgsl_sync_timeline *ktimeline,
355 unsigned int timestamp)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700356{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530357 unsigned long flags;
358 struct kgsl_sync_fence *kfence, *next;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700359
Lynus Vazc031a9b2017-01-25 13:00:13 +0530360 kref_get(&ktimeline->kref);
361
362 spin_lock_irqsave(&ktimeline->lock, flags);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700363 if (timestamp_cmp(timestamp, ktimeline->last_timestamp) > 0)
364 ktimeline->last_timestamp = timestamp;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700365
Lynus Vazc031a9b2017-01-25 13:00:13 +0530366 list_for_each_entry_safe(kfence, next, &ktimeline->child_list_head,
367 child_list) {
368 if (fence_is_signaled_locked(&kfence->fence)) {
369 list_del(&kfence->child_list);
370 fence_put(&kfence->fence);
371 }
372 }
373
374 spin_unlock_irqrestore(&ktimeline->lock, flags);
375 kgsl_sync_timeline_put(ktimeline);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700376}
377
378void kgsl_sync_timeline_destroy(struct kgsl_context *context)
379{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530380 kfree(context->ktimeline);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700381}
382
Lynus Vazc031a9b2017-01-25 13:00:13 +0530383static void kgsl_sync_timeline_release(struct kref *kref)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700384{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530385 struct kgsl_sync_timeline *ktimeline =
386 container_of(kref, struct kgsl_sync_timeline, kref);
387
388 /*
389 * Only put the context refcount here. The context destroy function
390 * will call kgsl_sync_timeline_destroy() to kfree it
391 */
392 kgsl_context_put(ktimeline->context);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700393}
394
Lynus Vazc031a9b2017-01-25 13:00:13 +0530395void kgsl_sync_timeline_put(struct kgsl_sync_timeline *ktimeline)
396{
397 if (ktimeline)
398 kref_put(&ktimeline->kref, kgsl_sync_timeline_release);
399}
400
401static const struct fence_ops kgsl_sync_fence_ops = {
402 .get_driver_name = kgsl_sync_fence_driver_name,
403 .get_timeline_name = kgsl_sync_timeline_name,
404 .enable_signaling = kgsl_enable_signaling,
405 .signaled = kgsl_sync_fence_has_signaled,
406 .wait = fence_default_wait,
407 .release = kgsl_sync_fence_release,
408
409 .fence_value_str = kgsl_sync_fence_value_str,
410 .timeline_value_str = kgsl_sync_timeline_value_str,
411};
412
413static void kgsl_sync_fence_callback(struct fence *fence, struct fence_cb *cb)
414{
415 struct kgsl_sync_fence_cb *kcb = (struct kgsl_sync_fence_cb *)cb;
416
417 kcb->func(kcb->priv);
418 fence_put(kcb->fence);
419 kfree(kcb);
420}
421
422struct kgsl_sync_fence_cb *kgsl_sync_fence_async_wait(int fd,
Shrenuj Bansala419c792016-10-20 14:05:11 -0700423 void (*func)(void *priv), void *priv)
424{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530425 struct kgsl_sync_fence_cb *kcb;
426 struct fence *fence;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700427 int status;
428
Lynus Vazc031a9b2017-01-25 13:00:13 +0530429 fence = sync_file_get_fence(fd);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700430 if (fence == NULL)
431 return ERR_PTR(-EINVAL);
432
Lynus Vazc031a9b2017-01-25 13:00:13 +0530433 /* create the callback */
434 kcb = kzalloc(sizeof(*kcb), GFP_ATOMIC);
435 if (kcb == NULL) {
436 fence_put(fence);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700437 return ERR_PTR(-ENOMEM);
438 }
439
Lynus Vazc031a9b2017-01-25 13:00:13 +0530440 kcb->fence = fence;
441 kcb->priv = priv;
442 kcb->func = func;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700443
444 /* if status then error or signaled */
Lynus Vazc031a9b2017-01-25 13:00:13 +0530445 status = fence_add_callback(fence, &kcb->fence_cb,
446 kgsl_sync_fence_callback);
447
Shrenuj Bansala419c792016-10-20 14:05:11 -0700448 if (status) {
Lynus Vazc031a9b2017-01-25 13:00:13 +0530449 kfree(kcb);
450 if (fence_is_signaled(fence))
451 kcb = ERR_PTR(status);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700452 else
Lynus Vazc031a9b2017-01-25 13:00:13 +0530453 kcb = NULL;
454 fence_put(fence);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700455 }
456
Lynus Vazc031a9b2017-01-25 13:00:13 +0530457 return kcb;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700458}
459
Lynus Vazc031a9b2017-01-25 13:00:13 +0530460int kgsl_sync_fence_async_cancel(struct kgsl_sync_fence_cb *kcb)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700461{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530462 if (kcb == NULL)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700463 return 0;
464
Lynus Vazc031a9b2017-01-25 13:00:13 +0530465 if (fence_remove_callback(kcb->fence, &kcb->fence_cb)) {
466 fence_put(kcb->fence);
467 kfree(kcb);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700468 return 1;
469 }
470 return 0;
471}
472
Shrenuj Bansala419c792016-10-20 14:05:11 -0700473struct kgsl_syncsource {
474 struct kref refcount;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530475 char name[KGSL_TIMELINE_NAME_LEN];
Shrenuj Bansala419c792016-10-20 14:05:11 -0700476 int id;
477 struct kgsl_process_private *private;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530478 struct list_head child_list_head;
479 spinlock_t lock;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700480};
481
Lynus Vazc031a9b2017-01-25 13:00:13 +0530482struct kgsl_syncsource_fence {
483 struct fence fence;
484 struct kgsl_syncsource *parent;
485 struct list_head child_list;
486};
487
488static const struct fence_ops kgsl_syncsource_fence_ops;
489
Shrenuj Bansala419c792016-10-20 14:05:11 -0700490long kgsl_ioctl_syncsource_create(struct kgsl_device_private *dev_priv,
491 unsigned int cmd, void *data)
492{
493 struct kgsl_syncsource *syncsource = NULL;
494 struct kgsl_syncsource_create *param = data;
495 int ret = -EINVAL;
496 int id = 0;
497 struct kgsl_process_private *private = dev_priv->process_priv;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530498 char name[KGSL_TIMELINE_NAME_LEN];
499
500 if (!kgsl_process_private_get(private))
501 return ret;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700502
503 syncsource = kzalloc(sizeof(*syncsource), GFP_KERNEL);
504 if (syncsource == NULL) {
505 ret = -ENOMEM;
506 goto out;
507 }
508
509 snprintf(name, sizeof(name), "kgsl-syncsource-pid-%d",
510 current->group_leader->pid);
511
Shrenuj Bansala419c792016-10-20 14:05:11 -0700512 kref_init(&syncsource->refcount);
Lynus Vazc031a9b2017-01-25 13:00:13 +0530513 strlcpy(syncsource->name, name, KGSL_TIMELINE_NAME_LEN);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700514 syncsource->private = private;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530515 INIT_LIST_HEAD(&syncsource->child_list_head);
516 spin_lock_init(&syncsource->lock);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700517
518 idr_preload(GFP_KERNEL);
519 spin_lock(&private->syncsource_lock);
520 id = idr_alloc(&private->syncsource_idr, syncsource, 1, 0, GFP_NOWAIT);
521 if (id > 0) {
522 syncsource->id = id;
523 param->id = id;
524 ret = 0;
525 } else {
526 ret = id;
527 }
528
529 spin_unlock(&private->syncsource_lock);
530 idr_preload_end();
531
532out:
533 if (ret) {
Lynus Vazc031a9b2017-01-25 13:00:13 +0530534 kgsl_process_private_put(private);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700535 kfree(syncsource);
536 }
537
538 return ret;
539}
540
541static struct kgsl_syncsource *
542kgsl_syncsource_get(struct kgsl_process_private *private, int id)
543{
544 int result = 0;
545 struct kgsl_syncsource *syncsource = NULL;
546
547 spin_lock(&private->syncsource_lock);
548
549 syncsource = idr_find(&private->syncsource_idr, id);
550 if (syncsource)
551 result = kref_get_unless_zero(&syncsource->refcount);
552
553 spin_unlock(&private->syncsource_lock);
554
555 return result ? syncsource : NULL;
556}
557
558static void kgsl_syncsource_destroy(struct kref *kref)
559{
560 struct kgsl_syncsource *syncsource = container_of(kref,
561 struct kgsl_syncsource,
562 refcount);
563
564 struct kgsl_process_private *private = syncsource->private;
565
Lynus Vazc031a9b2017-01-25 13:00:13 +0530566 /* Done with process private. Release the refcount */
567 kgsl_process_private_put(private);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700568
569 kfree(syncsource);
570}
571
572void kgsl_syncsource_put(struct kgsl_syncsource *syncsource)
573{
574 if (syncsource)
575 kref_put(&syncsource->refcount, kgsl_syncsource_destroy);
576}
577
Lynus Vazc031a9b2017-01-25 13:00:13 +0530578void kgsl_syncsource_cleanup(struct kgsl_process_private *private,
579 struct kgsl_syncsource *syncsource)
580{
581 struct kgsl_syncsource_fence *sfence, *next;
582
583 spin_lock(&private->syncsource_lock);
584 if (syncsource->id != 0) {
585 idr_remove(&private->syncsource_idr, syncsource->id);
586 syncsource->id = 0;
587 }
588 spin_unlock(&private->syncsource_lock);
589
590 /* Signal all fences to release any callbacks */
591 spin_lock(&syncsource->lock);
592
593 list_for_each_entry_safe(sfence, next, &syncsource->child_list_head,
594 child_list) {
595 fence_signal_locked(&sfence->fence);
596 list_del_init(&sfence->child_list);
597 }
598
599 spin_unlock(&syncsource->lock);
600
601 /* put reference from syncsource creation */
602 kgsl_syncsource_put(syncsource);
603}
604
Shrenuj Bansala419c792016-10-20 14:05:11 -0700605long kgsl_ioctl_syncsource_destroy(struct kgsl_device_private *dev_priv,
606 unsigned int cmd, void *data)
607{
608 struct kgsl_syncsource_destroy *param = data;
609 struct kgsl_syncsource *syncsource = NULL;
610 struct kgsl_process_private *private = dev_priv->process_priv;
611
612 spin_lock(&private->syncsource_lock);
613 syncsource = idr_find(&private->syncsource_idr, param->id);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700614 spin_unlock(&private->syncsource_lock);
615
616 if (syncsource == NULL)
617 return -EINVAL;
618
Lynus Vazc031a9b2017-01-25 13:00:13 +0530619 kgsl_syncsource_cleanup(private, syncsource);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700620 return 0;
621}
622
623long kgsl_ioctl_syncsource_create_fence(struct kgsl_device_private *dev_priv,
624 unsigned int cmd, void *data)
625{
626 struct kgsl_syncsource_create_fence *param = data;
627 struct kgsl_syncsource *syncsource = NULL;
628 int ret = -EINVAL;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530629 struct kgsl_syncsource_fence *sfence = NULL;
630 struct sync_file *sync_file = NULL;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700631 int fd = -1;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700632
Lynus Vazc031a9b2017-01-25 13:00:13 +0530633 /*
634 * Take a refcount that is released when the fence is released
635 * (or if fence can't be added to the syncsource).
636 */
Shrenuj Bansala419c792016-10-20 14:05:11 -0700637 syncsource = kgsl_syncsource_get(dev_priv->process_priv,
638 param->id);
639 if (syncsource == NULL)
640 goto out;
641
Lynus Vazc031a9b2017-01-25 13:00:13 +0530642 sfence = kzalloc(sizeof(*sfence), GFP_KERNEL);
643 if (sfence == NULL) {
644 ret = -ENOMEM;
645 goto out;
646 }
647 sfence->parent = syncsource;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700648
Lynus Vazc031a9b2017-01-25 13:00:13 +0530649 /* Use a new fence context for each fence */
650 fence_init(&sfence->fence, &kgsl_syncsource_fence_ops,
651 &syncsource->lock, fence_context_alloc(1), 1);
652
653 sync_file = sync_file_create(&sfence->fence);
654
655 if (sync_file == NULL) {
656 KGSL_DRV_ERR(dev_priv->device, "Create sync_file failed\n");
Shrenuj Bansala419c792016-10-20 14:05:11 -0700657 ret = -ENOMEM;
658 goto out;
659 }
660
661 fd = get_unused_fd_flags(0);
662 if (fd < 0) {
663 ret = -EBADF;
664 goto out;
665 }
666 ret = 0;
667
Lynus Vazc031a9b2017-01-25 13:00:13 +0530668 fd_install(fd, sync_file->file);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700669
670 param->fence_fd = fd;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530671
672 spin_lock(&syncsource->lock);
673 list_add_tail(&sfence->child_list, &syncsource->child_list_head);
674 spin_unlock(&syncsource->lock);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700675out:
676 if (ret) {
Lynus Vazc031a9b2017-01-25 13:00:13 +0530677 if (sync_file)
678 fput(sync_file->file);
679 else if (sfence)
680 fence_put(&sfence->fence);
681 kgsl_syncsource_put(syncsource);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700682 }
Lynus Vazc031a9b2017-01-25 13:00:13 +0530683
684 return ret;
685}
686
687static int kgsl_syncsource_signal(struct kgsl_syncsource *syncsource,
688 struct fence *fence)
689{
690 struct kgsl_syncsource_fence *sfence, *next;
691 int ret = -EINVAL;
692
693 spin_lock(&syncsource->lock);
694
695 list_for_each_entry_safe(sfence, next, &syncsource->child_list_head,
696 child_list) {
697 if (fence == &sfence->fence) {
698 fence_signal_locked(fence);
699 list_del_init(&sfence->child_list);
700
701 ret = 0;
702 break;
703 }
704 }
705
706 spin_unlock(&syncsource->lock);
707
Shrenuj Bansala419c792016-10-20 14:05:11 -0700708 return ret;
709}
710
711long kgsl_ioctl_syncsource_signal_fence(struct kgsl_device_private *dev_priv,
712 unsigned int cmd, void *data)
713{
714 int ret = -EINVAL;
715 struct kgsl_syncsource_signal_fence *param = data;
716 struct kgsl_syncsource *syncsource = NULL;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530717 struct fence *fence = NULL;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700718
719 syncsource = kgsl_syncsource_get(dev_priv->process_priv,
720 param->id);
721 if (syncsource == NULL)
722 goto out;
723
Lynus Vazc031a9b2017-01-25 13:00:13 +0530724 fence = sync_file_get_fence(param->fence_fd);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700725 if (fence == NULL) {
726 ret = -EBADF;
727 goto out;
728 }
729
Lynus Vazc031a9b2017-01-25 13:00:13 +0530730 ret = kgsl_syncsource_signal(syncsource, fence);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700731out:
732 if (fence)
Lynus Vazc031a9b2017-01-25 13:00:13 +0530733 fence_put(fence);
734 if (syncsource)
735 kgsl_syncsource_put(syncsource);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700736 return ret;
737}
Lynus Vazc031a9b2017-01-25 13:00:13 +0530738
739static void kgsl_syncsource_fence_release(struct fence *fence)
740{
741 struct kgsl_syncsource_fence *sfence =
742 (struct kgsl_syncsource_fence *)fence;
743
744 /* Signal if it's not signaled yet */
745 kgsl_syncsource_signal(sfence->parent, fence);
746
747 /* Release the refcount on the syncsource */
748 kgsl_syncsource_put(sfence->parent);
749
750 kfree(sfence);
751}
752
753static const char *kgsl_syncsource_get_timeline_name(struct fence *fence)
754{
755 struct kgsl_syncsource_fence *sfence =
756 (struct kgsl_syncsource_fence *)fence;
757 struct kgsl_syncsource *syncsource = sfence->parent;
758
759 return syncsource->name;
760}
761
762static bool kgsl_syncsource_enable_signaling(struct fence *fence)
763{
764 return true;
765}
766
767static const char *kgsl_syncsource_driver_name(struct fence *fence)
768{
769 return "kgsl-syncsource-timeline";
770}
771
772static const struct fence_ops kgsl_syncsource_fence_ops = {
773 .get_driver_name = kgsl_syncsource_driver_name,
774 .get_timeline_name = kgsl_syncsource_get_timeline_name,
775 .enable_signaling = kgsl_syncsource_enable_signaling,
776 .wait = fence_default_wait,
777 .release = kgsl_syncsource_fence_release,
778};
779
780void kgsl_dump_fence(struct kgsl_sync_fence_cb *handle,
781 char *fence_str, int len)
782{
783 struct fence *fence;
784 char *ptr = fence_str;
785 char *last = fence_str + len;
786
787 if (!handle || !handle->fence) {
788 snprintf(fence_str, len, "NULL");
789 return;
790 }
791
792 fence = handle->fence;
793
794 ptr += snprintf(ptr, last - ptr, "%s %s",
795 fence->ops->get_timeline_name(fence),
796 fence->ops->get_driver_name(fence));
797 if (ptr >= last)
798 return;
799
800 if (fence->ops->timeline_value_str &&
801 fence->ops->fence_value_str) {
802 char value[64];
803 bool success;
804
805 fence->ops->fence_value_str(fence, value, sizeof(value));
806 success = !!strlen(value);
807
808 if (success) {
809 ptr += snprintf(ptr, last - ptr, ": %s", value);
810 if (ptr >= last)
811 return;
812
813 fence->ops->timeline_value_str(fence, value,
814 sizeof(value));
815 ptr += snprintf(ptr, last - ptr, " / %s", value);
816 }
817 }
818}
819