blob: 9c078b6dc830aa9409396db842feebfe2ae7da88 [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
Lynus Vazc50d6202017-03-23 13:42:58 +0530155/* Only to be used if creating a related event failed */
156static void kgsl_sync_cancel(struct kgsl_sync_fence *kfence)
157{
158 spin_lock(&kfence->parent->lock);
159 if (!list_empty(&kfence->child_list)) {
160 list_del_init(&kfence->child_list);
161 fence_put(&kfence->fence);
162 }
163 spin_unlock(&kfence->parent->lock);
164}
165
Shrenuj Bansala419c792016-10-20 14:05:11 -0700166/**
167 * kgsl_add_fence_event - Create a new fence event
168 * @device - KGSL device to create the event on
169 * @timestamp - Timestamp to trigger the event
170 * @data - Return fence fd stored in struct kgsl_timestamp_event_fence
171 * @len - length of the fence event
172 * @owner - driver instance that owns this event
173 * @returns 0 on success or error code on error
174 *
175 * Create a fence and register an event to signal the fence when
176 * the timestamp expires
177 */
178
179int kgsl_add_fence_event(struct kgsl_device *device,
180 u32 context_id, u32 timestamp, void __user *data, int len,
181 struct kgsl_device_private *owner)
182{
183 struct kgsl_timestamp_event_fence priv;
184 struct kgsl_context *context;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530185 struct kgsl_sync_fence *kfence = NULL;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700186 int ret = -EINVAL;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700187 unsigned int cur;
188
189 priv.fence_fd = -1;
190
191 if (len != sizeof(priv))
192 return -EINVAL;
193
194 context = kgsl_context_get_owner(owner, context_id);
195
196 if (context == NULL)
197 return -EINVAL;
198
199 if (test_bit(KGSL_CONTEXT_PRIV_INVALID, &context->priv))
200 goto out;
201
Lynus Vazc031a9b2017-01-25 13:00:13 +0530202 kfence = kgsl_sync_fence_create(context, timestamp);
203 if (kfence == NULL) {
204 KGSL_DRV_CRIT_RATELIMIT(device,
205 "kgsl_sync_fence_create failed\n");
Shrenuj Bansala419c792016-10-20 14:05:11 -0700206 ret = -ENOMEM;
207 goto out;
208 }
209
210 priv.fence_fd = get_unused_fd_flags(0);
211 if (priv.fence_fd < 0) {
212 KGSL_DRV_CRIT_RATELIMIT(device,
213 "Unable to get a file descriptor: %d\n",
214 priv.fence_fd);
215 ret = priv.fence_fd;
216 goto out;
217 }
218
219 /*
220 * If the timestamp hasn't expired yet create an event to trigger it.
221 * Otherwise, just signal the fence - there is no reason to go through
222 * the effort of creating a fence we don't need.
223 */
224
225 kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED, &cur);
226
227 if (timestamp_cmp(cur, timestamp) >= 0) {
228 ret = 0;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530229 kgsl_sync_timeline_signal(context->ktimeline, cur);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700230 } else {
231 ret = _add_fence_event(device, context, timestamp);
232 if (ret)
233 goto out;
234 }
235
236 if (copy_to_user(data, &priv, sizeof(priv))) {
237 ret = -EFAULT;
238 goto out;
239 }
Lynus Vazc031a9b2017-01-25 13:00:13 +0530240 fd_install(priv.fence_fd, kfence->sync_file->file);
241
Shrenuj Bansala419c792016-10-20 14:05:11 -0700242out:
243 kgsl_context_put(context);
244 if (ret) {
245 if (priv.fence_fd >= 0)
246 put_unused_fd(priv.fence_fd);
247
Lynus Vazc031a9b2017-01-25 13:00:13 +0530248 if (kfence) {
Lynus Vazc50d6202017-03-23 13:42:58 +0530249 kgsl_sync_cancel(kfence);
Lynus Vazc031a9b2017-01-25 13:00:13 +0530250 /*
251 * Put the refcount of sync file. This will release
252 * kfence->fence as well.
253 */
254 fput(kfence->sync_file->file);
255 }
Shrenuj Bansala419c792016-10-20 14:05:11 -0700256 }
257 return ret;
258}
259
Lynus Vazc031a9b2017-01-25 13:00:13 +0530260static unsigned int kgsl_sync_fence_get_timestamp(
261 struct kgsl_sync_timeline *ktimeline,
262 enum kgsl_timestamp_type type)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700263{
264 unsigned int ret = 0;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700265
266 if (ktimeline->device == NULL)
267 return 0;
268
Lynus Vazc031a9b2017-01-25 13:00:13 +0530269 kgsl_readtimestamp(ktimeline->device, ktimeline->context, type, &ret);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700270
Shrenuj Bansala419c792016-10-20 14:05:11 -0700271 return ret;
272}
273
Lynus Vazc031a9b2017-01-25 13:00:13 +0530274static void kgsl_sync_timeline_value_str(struct fence *fence,
275 char *str, int size)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700276{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530277 struct kgsl_sync_fence *kfence = (struct kgsl_sync_fence *)fence;
278 struct kgsl_sync_timeline *ktimeline = kfence->parent;
279
280 unsigned int timestamp_retired;
281 unsigned int timestamp_queued;
282
283 if (!kref_get_unless_zero(&ktimeline->kref))
284 return;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700285
286 /*
287 * This callback can be called before the device and spinlock are
288 * initialized in struct kgsl_sync_timeline. kgsl_sync_get_timestamp()
289 * will check if device is NULL and return 0. Queued and retired
290 * timestamp of the context will be reported as 0, which is correct
291 * because the context and timeline are just getting initialized.
292 */
Lynus Vazc031a9b2017-01-25 13:00:13 +0530293 timestamp_retired = kgsl_sync_fence_get_timestamp(ktimeline,
294 KGSL_TIMESTAMP_RETIRED);
295 timestamp_queued = kgsl_sync_fence_get_timestamp(ktimeline,
296 KGSL_TIMESTAMP_QUEUED);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700297
298 snprintf(str, size, "%u queued:%u retired:%u",
299 ktimeline->last_timestamp,
300 timestamp_queued, timestamp_retired);
Lynus Vazc031a9b2017-01-25 13:00:13 +0530301
302 kgsl_sync_timeline_put(ktimeline);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700303}
304
Lynus Vazc031a9b2017-01-25 13:00:13 +0530305static void kgsl_sync_fence_value_str(struct fence *fence, char *str, int size)
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;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700308
Lynus Vazc031a9b2017-01-25 13:00:13 +0530309 snprintf(str, size, "%u", kfence->timestamp);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700310}
311
Lynus Vazc031a9b2017-01-25 13:00:13 +0530312static const char *kgsl_sync_fence_driver_name(struct fence *fence)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700313{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530314 return "kgsl-timeline";
Shrenuj Bansala419c792016-10-20 14:05:11 -0700315}
316
Lynus Vazc031a9b2017-01-25 13:00:13 +0530317static const char *kgsl_sync_timeline_name(struct fence *fence)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700318{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530319 struct kgsl_sync_fence *kfence = (struct kgsl_sync_fence *)fence;
320 struct kgsl_sync_timeline *ktimeline = kfence->parent;
321
322 return ktimeline->name;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700323}
Shrenuj Bansala419c792016-10-20 14:05:11 -0700324
325int kgsl_sync_timeline_create(struct kgsl_context *context)
326{
327 struct kgsl_sync_timeline *ktimeline;
328
329 /*
330 * Generate a name which includes the thread name, thread id, process
331 * name, process id, and context id. This makes it possible to
332 * identify the context of a timeline in the sync dump.
333 */
Lynus Vazc031a9b2017-01-25 13:00:13 +0530334 char ktimeline_name[sizeof(ktimeline->name)] = {};
335
336 /* Put context when timeline is released */
337 if (!_kgsl_context_get(context))
338 return -ENOENT;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700339
340 snprintf(ktimeline_name, sizeof(ktimeline_name),
341 "%s_%.15s(%d)-%.15s(%d)-%d",
342 context->device->name,
343 current->group_leader->comm, current->group_leader->pid,
344 current->comm, current->pid, context->id);
345
Lynus Vazc031a9b2017-01-25 13:00:13 +0530346 ktimeline = kzalloc(sizeof(*ktimeline), GFP_KERNEL);
347 if (ktimeline == NULL) {
348 kgsl_context_put(context);
349 return -ENOMEM;
350 }
Shrenuj Bansala419c792016-10-20 14:05:11 -0700351
Lynus Vazc031a9b2017-01-25 13:00:13 +0530352 kref_init(&ktimeline->kref);
353 strlcpy(ktimeline->name, ktimeline_name, KGSL_TIMELINE_NAME_LEN);
354 ktimeline->fence_context = fence_context_alloc(1);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700355 ktimeline->last_timestamp = 0;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530356 INIT_LIST_HEAD(&ktimeline->child_list_head);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700357 spin_lock_init(&ktimeline->lock);
Lynus Vazc031a9b2017-01-25 13:00:13 +0530358 ktimeline->device = context->device;
359 ktimeline->context = context;
360
361 context->ktimeline = ktimeline;
362
Shrenuj Bansala419c792016-10-20 14:05:11 -0700363 return 0;
364}
365
Lynus Vazc031a9b2017-01-25 13:00:13 +0530366static void kgsl_sync_timeline_signal(struct kgsl_sync_timeline *ktimeline,
367 unsigned int timestamp)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700368{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530369 unsigned long flags;
370 struct kgsl_sync_fence *kfence, *next;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700371
Lynus Vazc031a9b2017-01-25 13:00:13 +0530372 kref_get(&ktimeline->kref);
373
374 spin_lock_irqsave(&ktimeline->lock, flags);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700375 if (timestamp_cmp(timestamp, ktimeline->last_timestamp) > 0)
376 ktimeline->last_timestamp = timestamp;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700377
Lynus Vazc031a9b2017-01-25 13:00:13 +0530378 list_for_each_entry_safe(kfence, next, &ktimeline->child_list_head,
379 child_list) {
380 if (fence_is_signaled_locked(&kfence->fence)) {
Lynus Vazc50d6202017-03-23 13:42:58 +0530381 list_del_init(&kfence->child_list);
Lynus Vazc031a9b2017-01-25 13:00:13 +0530382 fence_put(&kfence->fence);
383 }
384 }
385
386 spin_unlock_irqrestore(&ktimeline->lock, flags);
387 kgsl_sync_timeline_put(ktimeline);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700388}
389
390void kgsl_sync_timeline_destroy(struct kgsl_context *context)
391{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530392 kfree(context->ktimeline);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700393}
394
Lynus Vazc031a9b2017-01-25 13:00:13 +0530395static void kgsl_sync_timeline_release(struct kref *kref)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700396{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530397 struct kgsl_sync_timeline *ktimeline =
398 container_of(kref, struct kgsl_sync_timeline, kref);
399
400 /*
401 * Only put the context refcount here. The context destroy function
402 * will call kgsl_sync_timeline_destroy() to kfree it
403 */
404 kgsl_context_put(ktimeline->context);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700405}
406
Lynus Vazc031a9b2017-01-25 13:00:13 +0530407void kgsl_sync_timeline_put(struct kgsl_sync_timeline *ktimeline)
408{
409 if (ktimeline)
410 kref_put(&ktimeline->kref, kgsl_sync_timeline_release);
411}
412
413static const struct fence_ops kgsl_sync_fence_ops = {
414 .get_driver_name = kgsl_sync_fence_driver_name,
415 .get_timeline_name = kgsl_sync_timeline_name,
416 .enable_signaling = kgsl_enable_signaling,
417 .signaled = kgsl_sync_fence_has_signaled,
418 .wait = fence_default_wait,
419 .release = kgsl_sync_fence_release,
420
421 .fence_value_str = kgsl_sync_fence_value_str,
422 .timeline_value_str = kgsl_sync_timeline_value_str,
423};
424
425static void kgsl_sync_fence_callback(struct fence *fence, struct fence_cb *cb)
426{
427 struct kgsl_sync_fence_cb *kcb = (struct kgsl_sync_fence_cb *)cb;
428
429 kcb->func(kcb->priv);
430 fence_put(kcb->fence);
431 kfree(kcb);
432}
433
434struct kgsl_sync_fence_cb *kgsl_sync_fence_async_wait(int fd,
Shrenuj Bansala419c792016-10-20 14:05:11 -0700435 void (*func)(void *priv), void *priv)
436{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530437 struct kgsl_sync_fence_cb *kcb;
438 struct fence *fence;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700439 int status;
440
Lynus Vazc031a9b2017-01-25 13:00:13 +0530441 fence = sync_file_get_fence(fd);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700442 if (fence == NULL)
443 return ERR_PTR(-EINVAL);
444
Lynus Vazc031a9b2017-01-25 13:00:13 +0530445 /* create the callback */
446 kcb = kzalloc(sizeof(*kcb), GFP_ATOMIC);
447 if (kcb == NULL) {
448 fence_put(fence);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700449 return ERR_PTR(-ENOMEM);
450 }
451
Lynus Vazc031a9b2017-01-25 13:00:13 +0530452 kcb->fence = fence;
453 kcb->priv = priv;
454 kcb->func = func;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700455
456 /* if status then error or signaled */
Lynus Vazc031a9b2017-01-25 13:00:13 +0530457 status = fence_add_callback(fence, &kcb->fence_cb,
458 kgsl_sync_fence_callback);
459
Shrenuj Bansala419c792016-10-20 14:05:11 -0700460 if (status) {
Lynus Vazc031a9b2017-01-25 13:00:13 +0530461 kfree(kcb);
Harshdeep Dhatt0ed60a42017-04-20 16:41:50 -0600462 if (!fence_is_signaled(fence))
Lynus Vazc031a9b2017-01-25 13:00:13 +0530463 kcb = ERR_PTR(status);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700464 else
Lynus Vazc031a9b2017-01-25 13:00:13 +0530465 kcb = NULL;
466 fence_put(fence);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700467 }
468
Lynus Vazc031a9b2017-01-25 13:00:13 +0530469 return kcb;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700470}
471
Lynus Vazc031a9b2017-01-25 13:00:13 +0530472int kgsl_sync_fence_async_cancel(struct kgsl_sync_fence_cb *kcb)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700473{
Lynus Vazc031a9b2017-01-25 13:00:13 +0530474 if (kcb == NULL)
Shrenuj Bansala419c792016-10-20 14:05:11 -0700475 return 0;
476
Lynus Vazc031a9b2017-01-25 13:00:13 +0530477 if (fence_remove_callback(kcb->fence, &kcb->fence_cb)) {
478 fence_put(kcb->fence);
479 kfree(kcb);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700480 return 1;
481 }
482 return 0;
483}
484
Shrenuj Bansala419c792016-10-20 14:05:11 -0700485struct kgsl_syncsource {
486 struct kref refcount;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530487 char name[KGSL_TIMELINE_NAME_LEN];
Shrenuj Bansala419c792016-10-20 14:05:11 -0700488 int id;
489 struct kgsl_process_private *private;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530490 struct list_head child_list_head;
491 spinlock_t lock;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700492};
493
Lynus Vazc031a9b2017-01-25 13:00:13 +0530494struct kgsl_syncsource_fence {
495 struct fence fence;
496 struct kgsl_syncsource *parent;
497 struct list_head child_list;
498};
499
500static const struct fence_ops kgsl_syncsource_fence_ops;
501
Shrenuj Bansala419c792016-10-20 14:05:11 -0700502long kgsl_ioctl_syncsource_create(struct kgsl_device_private *dev_priv,
503 unsigned int cmd, void *data)
504{
505 struct kgsl_syncsource *syncsource = NULL;
506 struct kgsl_syncsource_create *param = data;
507 int ret = -EINVAL;
508 int id = 0;
509 struct kgsl_process_private *private = dev_priv->process_priv;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530510 char name[KGSL_TIMELINE_NAME_LEN];
511
512 if (!kgsl_process_private_get(private))
513 return ret;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700514
515 syncsource = kzalloc(sizeof(*syncsource), GFP_KERNEL);
516 if (syncsource == NULL) {
517 ret = -ENOMEM;
518 goto out;
519 }
520
521 snprintf(name, sizeof(name), "kgsl-syncsource-pid-%d",
522 current->group_leader->pid);
523
Shrenuj Bansala419c792016-10-20 14:05:11 -0700524 kref_init(&syncsource->refcount);
Lynus Vazc031a9b2017-01-25 13:00:13 +0530525 strlcpy(syncsource->name, name, KGSL_TIMELINE_NAME_LEN);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700526 syncsource->private = private;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530527 INIT_LIST_HEAD(&syncsource->child_list_head);
528 spin_lock_init(&syncsource->lock);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700529
530 idr_preload(GFP_KERNEL);
531 spin_lock(&private->syncsource_lock);
532 id = idr_alloc(&private->syncsource_idr, syncsource, 1, 0, GFP_NOWAIT);
533 if (id > 0) {
534 syncsource->id = id;
535 param->id = id;
536 ret = 0;
537 } else {
538 ret = id;
539 }
540
541 spin_unlock(&private->syncsource_lock);
542 idr_preload_end();
543
544out:
545 if (ret) {
Lynus Vazc031a9b2017-01-25 13:00:13 +0530546 kgsl_process_private_put(private);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700547 kfree(syncsource);
548 }
549
550 return ret;
551}
552
553static struct kgsl_syncsource *
554kgsl_syncsource_get(struct kgsl_process_private *private, int id)
555{
556 int result = 0;
557 struct kgsl_syncsource *syncsource = NULL;
558
559 spin_lock(&private->syncsource_lock);
560
561 syncsource = idr_find(&private->syncsource_idr, id);
562 if (syncsource)
563 result = kref_get_unless_zero(&syncsource->refcount);
564
565 spin_unlock(&private->syncsource_lock);
566
567 return result ? syncsource : NULL;
568}
569
570static void kgsl_syncsource_destroy(struct kref *kref)
571{
572 struct kgsl_syncsource *syncsource = container_of(kref,
573 struct kgsl_syncsource,
574 refcount);
575
576 struct kgsl_process_private *private = syncsource->private;
577
Lynus Vazc031a9b2017-01-25 13:00:13 +0530578 /* Done with process private. Release the refcount */
579 kgsl_process_private_put(private);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700580
581 kfree(syncsource);
582}
583
584void kgsl_syncsource_put(struct kgsl_syncsource *syncsource)
585{
586 if (syncsource)
587 kref_put(&syncsource->refcount, kgsl_syncsource_destroy);
588}
589
Lynus Vazc031a9b2017-01-25 13:00:13 +0530590void kgsl_syncsource_cleanup(struct kgsl_process_private *private,
591 struct kgsl_syncsource *syncsource)
592{
593 struct kgsl_syncsource_fence *sfence, *next;
594
595 spin_lock(&private->syncsource_lock);
596 if (syncsource->id != 0) {
597 idr_remove(&private->syncsource_idr, syncsource->id);
598 syncsource->id = 0;
599 }
600 spin_unlock(&private->syncsource_lock);
601
602 /* Signal all fences to release any callbacks */
603 spin_lock(&syncsource->lock);
604
605 list_for_each_entry_safe(sfence, next, &syncsource->child_list_head,
606 child_list) {
607 fence_signal_locked(&sfence->fence);
608 list_del_init(&sfence->child_list);
609 }
610
611 spin_unlock(&syncsource->lock);
612
613 /* put reference from syncsource creation */
614 kgsl_syncsource_put(syncsource);
615}
616
Shrenuj Bansala419c792016-10-20 14:05:11 -0700617long kgsl_ioctl_syncsource_destroy(struct kgsl_device_private *dev_priv,
618 unsigned int cmd, void *data)
619{
620 struct kgsl_syncsource_destroy *param = data;
621 struct kgsl_syncsource *syncsource = NULL;
622 struct kgsl_process_private *private = dev_priv->process_priv;
623
624 spin_lock(&private->syncsource_lock);
625 syncsource = idr_find(&private->syncsource_idr, param->id);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700626 spin_unlock(&private->syncsource_lock);
627
628 if (syncsource == NULL)
629 return -EINVAL;
630
Lynus Vazc031a9b2017-01-25 13:00:13 +0530631 kgsl_syncsource_cleanup(private, syncsource);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700632 return 0;
633}
634
635long kgsl_ioctl_syncsource_create_fence(struct kgsl_device_private *dev_priv,
636 unsigned int cmd, void *data)
637{
638 struct kgsl_syncsource_create_fence *param = data;
639 struct kgsl_syncsource *syncsource = NULL;
640 int ret = -EINVAL;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530641 struct kgsl_syncsource_fence *sfence = NULL;
642 struct sync_file *sync_file = NULL;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700643 int fd = -1;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700644
Lynus Vazc031a9b2017-01-25 13:00:13 +0530645 /*
646 * Take a refcount that is released when the fence is released
647 * (or if fence can't be added to the syncsource).
648 */
Shrenuj Bansala419c792016-10-20 14:05:11 -0700649 syncsource = kgsl_syncsource_get(dev_priv->process_priv,
650 param->id);
651 if (syncsource == NULL)
652 goto out;
653
Lynus Vazc031a9b2017-01-25 13:00:13 +0530654 sfence = kzalloc(sizeof(*sfence), GFP_KERNEL);
655 if (sfence == NULL) {
656 ret = -ENOMEM;
657 goto out;
658 }
659 sfence->parent = syncsource;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700660
Lynus Vazc031a9b2017-01-25 13:00:13 +0530661 /* Use a new fence context for each fence */
662 fence_init(&sfence->fence, &kgsl_syncsource_fence_ops,
663 &syncsource->lock, fence_context_alloc(1), 1);
664
665 sync_file = sync_file_create(&sfence->fence);
666
667 if (sync_file == NULL) {
668 KGSL_DRV_ERR(dev_priv->device, "Create sync_file failed\n");
Shrenuj Bansala419c792016-10-20 14:05:11 -0700669 ret = -ENOMEM;
670 goto out;
671 }
672
673 fd = get_unused_fd_flags(0);
674 if (fd < 0) {
675 ret = -EBADF;
676 goto out;
677 }
678 ret = 0;
679
Lynus Vazc031a9b2017-01-25 13:00:13 +0530680 fd_install(fd, sync_file->file);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700681
682 param->fence_fd = fd;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530683
684 spin_lock(&syncsource->lock);
685 list_add_tail(&sfence->child_list, &syncsource->child_list_head);
686 spin_unlock(&syncsource->lock);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700687out:
688 if (ret) {
Lynus Vazc031a9b2017-01-25 13:00:13 +0530689 if (sync_file)
690 fput(sync_file->file);
691 else if (sfence)
692 fence_put(&sfence->fence);
693 kgsl_syncsource_put(syncsource);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700694 }
Lynus Vazc031a9b2017-01-25 13:00:13 +0530695
696 return ret;
697}
698
699static int kgsl_syncsource_signal(struct kgsl_syncsource *syncsource,
700 struct fence *fence)
701{
702 struct kgsl_syncsource_fence *sfence, *next;
703 int ret = -EINVAL;
704
705 spin_lock(&syncsource->lock);
706
707 list_for_each_entry_safe(sfence, next, &syncsource->child_list_head,
708 child_list) {
709 if (fence == &sfence->fence) {
710 fence_signal_locked(fence);
711 list_del_init(&sfence->child_list);
712
713 ret = 0;
714 break;
715 }
716 }
717
718 spin_unlock(&syncsource->lock);
719
Shrenuj Bansala419c792016-10-20 14:05:11 -0700720 return ret;
721}
722
723long kgsl_ioctl_syncsource_signal_fence(struct kgsl_device_private *dev_priv,
724 unsigned int cmd, void *data)
725{
726 int ret = -EINVAL;
727 struct kgsl_syncsource_signal_fence *param = data;
728 struct kgsl_syncsource *syncsource = NULL;
Lynus Vazc031a9b2017-01-25 13:00:13 +0530729 struct fence *fence = NULL;
Shrenuj Bansala419c792016-10-20 14:05:11 -0700730
731 syncsource = kgsl_syncsource_get(dev_priv->process_priv,
732 param->id);
733 if (syncsource == NULL)
734 goto out;
735
Lynus Vazc031a9b2017-01-25 13:00:13 +0530736 fence = sync_file_get_fence(param->fence_fd);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700737 if (fence == NULL) {
738 ret = -EBADF;
739 goto out;
740 }
741
Lynus Vazc031a9b2017-01-25 13:00:13 +0530742 ret = kgsl_syncsource_signal(syncsource, fence);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700743out:
744 if (fence)
Lynus Vazc031a9b2017-01-25 13:00:13 +0530745 fence_put(fence);
746 if (syncsource)
747 kgsl_syncsource_put(syncsource);
Shrenuj Bansala419c792016-10-20 14:05:11 -0700748 return ret;
749}
Lynus Vazc031a9b2017-01-25 13:00:13 +0530750
751static void kgsl_syncsource_fence_release(struct fence *fence)
752{
753 struct kgsl_syncsource_fence *sfence =
754 (struct kgsl_syncsource_fence *)fence;
755
756 /* Signal if it's not signaled yet */
757 kgsl_syncsource_signal(sfence->parent, fence);
758
759 /* Release the refcount on the syncsource */
760 kgsl_syncsource_put(sfence->parent);
761
762 kfree(sfence);
763}
764
765static const char *kgsl_syncsource_get_timeline_name(struct fence *fence)
766{
767 struct kgsl_syncsource_fence *sfence =
768 (struct kgsl_syncsource_fence *)fence;
769 struct kgsl_syncsource *syncsource = sfence->parent;
770
771 return syncsource->name;
772}
773
774static bool kgsl_syncsource_enable_signaling(struct fence *fence)
775{
776 return true;
777}
778
779static const char *kgsl_syncsource_driver_name(struct fence *fence)
780{
781 return "kgsl-syncsource-timeline";
782}
783
784static const struct fence_ops kgsl_syncsource_fence_ops = {
785 .get_driver_name = kgsl_syncsource_driver_name,
786 .get_timeline_name = kgsl_syncsource_get_timeline_name,
787 .enable_signaling = kgsl_syncsource_enable_signaling,
788 .wait = fence_default_wait,
789 .release = kgsl_syncsource_fence_release,
790};
791
792void kgsl_dump_fence(struct kgsl_sync_fence_cb *handle,
793 char *fence_str, int len)
794{
795 struct fence *fence;
796 char *ptr = fence_str;
797 char *last = fence_str + len;
798
799 if (!handle || !handle->fence) {
800 snprintf(fence_str, len, "NULL");
801 return;
802 }
803
804 fence = handle->fence;
805
806 ptr += snprintf(ptr, last - ptr, "%s %s",
807 fence->ops->get_timeline_name(fence),
808 fence->ops->get_driver_name(fence));
809 if (ptr >= last)
810 return;
811
812 if (fence->ops->timeline_value_str &&
813 fence->ops->fence_value_str) {
814 char value[64];
815 bool success;
816
817 fence->ops->fence_value_str(fence, value, sizeof(value));
818 success = !!strlen(value);
819
820 if (success) {
821 ptr += snprintf(ptr, last - ptr, ": %s", value);
822 if (ptr >= last)
823 return;
824
825 fence->ops->timeline_value_str(fence, value,
826 sizeof(value));
827 ptr += snprintf(ptr, last - ptr, " / %s", value);
828 }
829 }
830}
831