2 * Copyright (C) 2013-2016 ARM Limited. All rights reserved.
4 * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
7 * A copy of the licence is included with the program, and can also be obtained from Free Software
8 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
11 #include "mali_timeline.h"
12 #include "mali_kernel_common.h"
13 #include "mali_scheduler.h"
14 #include "mali_soft_job.h"
15 #include "mali_timeline_fence_wait.h"
16 #include "mali_timeline_sync_fence.h"
17 #include "mali_executor.h"
18 #include "mali_pp_job.h"
20 #define MALI_TIMELINE_SYSTEM_LOCKED(system) (mali_spinlock_reentrant_is_held((system)->spinlock, _mali_osk_get_tid()))
23 * Following three elements are used to record how many
24 * gp, physical pp or virtual pp jobs are delayed in the whole
25 * timeline system, we can use these three value to decide
26 * if need to deactivate idle group.
28 _mali_osk_atomic_t gp_tracker_count;
29 _mali_osk_atomic_t phy_pp_tracker_count;
30 _mali_osk_atomic_t virt_pp_tracker_count;
32 static mali_scheduler_mask mali_timeline_system_release_waiter(struct mali_timeline_system *system,
33 struct mali_timeline_waiter *waiter);
35 #if defined(CONFIG_SYNC)
36 #include <linux/version.h>
37 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
38 #include <linux/list.h>
39 #include <linux/workqueue.h>
40 #include <linux/spinlock.h>
42 struct mali_deferred_fence_put_entry {
43 struct hlist_node list;
44 struct sync_fence *fence;
47 static HLIST_HEAD(mali_timeline_sync_fence_to_free_list);
48 static DEFINE_SPINLOCK(mali_timeline_sync_fence_to_free_lock);
50 static void put_sync_fences(struct work_struct *ignore)
52 struct hlist_head list;
53 struct hlist_node *tmp, *pos;
55 struct mali_deferred_fence_put_entry *o;
57 spin_lock_irqsave(&mali_timeline_sync_fence_to_free_lock, flags);
58 hlist_move_list(&mali_timeline_sync_fence_to_free_list, &list);
59 spin_unlock_irqrestore(&mali_timeline_sync_fence_to_free_lock, flags);
61 hlist_for_each_entry_safe(o, pos, tmp, &list, list) {
62 sync_fence_put(o->fence);
67 static DECLARE_DELAYED_WORK(delayed_sync_fence_put, put_sync_fences);
68 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) */
70 /* Callback that is called when a sync fence a tracker is waiting on is signaled. */
71 static void mali_timeline_sync_fence_callback(struct sync_fence *sync_fence, struct sync_fence_waiter *sync_fence_waiter)
73 struct mali_timeline_system *system;
74 struct mali_timeline_waiter *waiter;
75 struct mali_timeline_tracker *tracker;
76 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
77 u32 tid = _mali_osk_get_tid();
78 mali_bool is_aborting = MALI_FALSE;
79 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
80 int fence_status = sync_fence->status;
82 int fence_status = atomic_read(&sync_fence->status);
85 MALI_DEBUG_ASSERT_POINTER(sync_fence);
86 MALI_DEBUG_ASSERT_POINTER(sync_fence_waiter);
88 tracker = _MALI_OSK_CONTAINER_OF(sync_fence_waiter, struct mali_timeline_tracker, sync_fence_waiter);
89 MALI_DEBUG_ASSERT_POINTER(tracker);
91 system = tracker->system;
92 MALI_DEBUG_ASSERT_POINTER(system);
93 MALI_DEBUG_ASSERT_POINTER(system->session);
95 mali_spinlock_reentrant_wait(system->spinlock, tid);
97 is_aborting = system->session->is_aborting;
98 if (!is_aborting && (0 > fence_status)) {
99 MALI_PRINT_ERROR(("Mali Timeline: sync fence fd %d signaled with error %d\n", tracker->fence.sync_fd, fence_status));
100 tracker->activation_error |= MALI_TIMELINE_ACTIVATION_ERROR_SYNC_BIT;
103 waiter = tracker->waiter_sync;
104 MALI_DEBUG_ASSERT_POINTER(waiter);
106 tracker->sync_fence = NULL;
107 tracker->fence.sync_fd = -1;
109 schedule_mask |= mali_timeline_system_release_waiter(system, waiter);
111 /* If aborting, wake up sleepers that are waiting for sync fence callbacks to complete. */
113 _mali_osk_wait_queue_wake_up(system->wait_queue);
116 mali_spinlock_reentrant_signal(system->spinlock, tid);
119 * Older versions of Linux, before 3.5, doesn't support fput() in interrupt
120 * context. For those older kernels, allocate a list object and put the
121 * fence object on that and defer the call to sync_fence_put() to a workqueue.
123 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
125 struct mali_deferred_fence_put_entry *obj;
127 obj = kzalloc(sizeof(struct mali_deferred_fence_put_entry), GFP_ATOMIC);
130 mali_bool schedule = MALI_FALSE;
132 obj->fence = sync_fence;
134 spin_lock_irqsave(&mali_timeline_sync_fence_to_free_lock, flags);
135 if (hlist_empty(&mali_timeline_sync_fence_to_free_list))
136 schedule = MALI_TRUE;
137 hlist_add_head(&obj->list, &mali_timeline_sync_fence_to_free_list);
138 spin_unlock_irqrestore(&mali_timeline_sync_fence_to_free_lock, flags);
141 schedule_delayed_work(&delayed_sync_fence_put, 0);
145 sync_fence_put(sync_fence);
146 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) */
149 mali_executor_schedule_from_mask(schedule_mask, MALI_TRUE);
152 #endif /* defined(CONFIG_SYNC) */
154 static mali_scheduler_mask mali_timeline_tracker_time_out(struct mali_timeline_tracker *tracker)
156 MALI_DEBUG_ASSERT_POINTER(tracker);
157 MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_SOFT == tracker->type);
159 return mali_soft_job_system_timeout_job((struct mali_soft_job *) tracker->job);
162 static void mali_timeline_timer_callback(void *data)
164 struct mali_timeline_system *system;
165 struct mali_timeline_tracker *tracker;
166 struct mali_timeline *timeline;
167 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
168 u32 tid = _mali_osk_get_tid();
170 timeline = (struct mali_timeline *) data;
171 MALI_DEBUG_ASSERT_POINTER(timeline);
173 system = timeline->system;
174 MALI_DEBUG_ASSERT_POINTER(system);
176 mali_spinlock_reentrant_wait(system->spinlock, tid);
178 if (!system->timer_enabled) {
179 mali_spinlock_reentrant_signal(system->spinlock, tid);
183 tracker = timeline->tracker_tail;
184 timeline->timer_active = MALI_FALSE;
186 if (NULL != tracker && MALI_TRUE == tracker->timer_active) {
187 /* This is likely the delayed work that has been schedule out before cancelled. */
188 if (MALI_TIMELINE_TIMEOUT_HZ > (_mali_osk_time_tickcount() - tracker->os_tick_activate)) {
189 mali_spinlock_reentrant_signal(system->spinlock, tid);
193 schedule_mask = mali_timeline_tracker_time_out(tracker);
194 tracker->timer_active = MALI_FALSE;
196 MALI_PRINT_ERROR(("Mali Timeline: Soft job timer callback without a waiting tracker.\n"));
199 mali_spinlock_reentrant_signal(system->spinlock, tid);
201 mali_executor_schedule_from_mask(schedule_mask, MALI_FALSE);
204 void mali_timeline_system_stop_timer(struct mali_timeline_system *system)
207 u32 tid = _mali_osk_get_tid();
209 MALI_DEBUG_ASSERT_POINTER(system);
211 mali_spinlock_reentrant_wait(system->spinlock, tid);
212 system->timer_enabled = MALI_FALSE;
213 mali_spinlock_reentrant_signal(system->spinlock, tid);
215 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
216 struct mali_timeline *timeline = system->timelines[i];
218 MALI_DEBUG_ASSERT_POINTER(timeline);
220 if (NULL != timeline->delayed_work) {
221 _mali_osk_wq_delayed_cancel_work_sync(timeline->delayed_work);
222 timeline->timer_active = MALI_FALSE;
227 static void mali_timeline_destroy(struct mali_timeline *timeline)
229 MALI_DEBUG_ASSERT_POINTER(timeline);
230 if (NULL != timeline) {
231 /* Assert that the timeline object has been properly cleaned up before destroying it. */
232 MALI_DEBUG_ASSERT(timeline->point_oldest == timeline->point_next);
233 MALI_DEBUG_ASSERT(NULL == timeline->tracker_head);
234 MALI_DEBUG_ASSERT(NULL == timeline->tracker_tail);
235 MALI_DEBUG_ASSERT(NULL == timeline->waiter_head);
236 MALI_DEBUG_ASSERT(NULL == timeline->waiter_tail);
237 MALI_DEBUG_ASSERT(NULL != timeline->system);
238 MALI_DEBUG_ASSERT(MALI_TIMELINE_MAX > timeline->id);
240 if (NULL != timeline->delayed_work) {
241 _mali_osk_wq_delayed_cancel_work_sync(timeline->delayed_work);
242 _mali_osk_wq_delayed_delete_work_nonflush(timeline->delayed_work);
245 #if defined(CONFIG_SYNC)
246 if (NULL != timeline->sync_tl) {
247 sync_timeline_destroy(timeline->sync_tl);
249 #endif /* defined(CONFIG_SYNC) */
252 _mali_osk_free(timeline);
257 static struct mali_timeline *mali_timeline_create(struct mali_timeline_system *system, enum mali_timeline_id id)
259 struct mali_timeline *timeline;
261 MALI_DEBUG_ASSERT_POINTER(system);
262 MALI_DEBUG_ASSERT(id < MALI_TIMELINE_MAX);
264 timeline = (struct mali_timeline *) _mali_osk_calloc(1, sizeof(struct mali_timeline));
265 if (NULL == timeline) {
269 /* Initially the timeline is empty. */
270 #if defined(MALI_TIMELINE_DEBUG_START_POINT)
271 /* Start the timeline a bit before wrapping when debugging. */
272 timeline->point_next = UINT_MAX - MALI_TIMELINE_MAX_POINT_SPAN - 128;
274 timeline->point_next = 1;
276 timeline->point_oldest = timeline->point_next;
278 /* The tracker and waiter lists will initially be empty. */
280 timeline->system = system;
283 timeline->delayed_work = _mali_osk_wq_delayed_create_work(mali_timeline_timer_callback, timeline);
284 if (NULL == timeline->delayed_work) {
285 mali_timeline_destroy(timeline);
289 timeline->timer_active = MALI_FALSE;
291 #if defined(CONFIG_SYNC)
293 char timeline_name[32];
296 case MALI_TIMELINE_GP:
297 _mali_osk_snprintf(timeline_name, 32, "mali-%u-gp", _mali_osk_get_pid());
299 case MALI_TIMELINE_PP:
300 _mali_osk_snprintf(timeline_name, 32, "mali-%u-pp", _mali_osk_get_pid());
302 case MALI_TIMELINE_SOFT:
303 _mali_osk_snprintf(timeline_name, 32, "mali-%u-soft", _mali_osk_get_pid());
306 MALI_PRINT_ERROR(("Mali Timeline: Invalid timeline id %d\n", id));
307 mali_timeline_destroy(timeline);
311 timeline->destroyed = MALI_FALSE;
313 timeline->sync_tl = mali_sync_timeline_create(timeline, timeline_name);
314 if (NULL == timeline->sync_tl) {
315 mali_timeline_destroy(timeline);
319 timeline->spinlock = mali_spinlock_reentrant_init(_MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM);
320 if (NULL == timeline->spinlock) {
321 mali_timeline_destroy(timeline);
325 #endif /* defined(CONFIG_SYNC) */
330 static void mali_timeline_insert_tracker(struct mali_timeline *timeline, struct mali_timeline_tracker *tracker)
332 MALI_DEBUG_ASSERT_POINTER(timeline);
333 MALI_DEBUG_ASSERT_POINTER(tracker);
335 if (mali_timeline_is_full(timeline)) {
336 /* Don't add tracker if timeline is full. */
337 tracker->point = MALI_TIMELINE_NO_POINT;
341 tracker->timeline = timeline;
342 tracker->point = timeline->point_next;
344 /* Find next available point. */
345 timeline->point_next++;
346 if (MALI_TIMELINE_NO_POINT == timeline->point_next) {
347 timeline->point_next++;
350 MALI_DEBUG_ASSERT(!mali_timeline_is_empty(timeline));
352 if (MALI_TIMELINE_TRACKER_GP == tracker->type) {
353 _mali_osk_atomic_inc(&gp_tracker_count);
354 } else if (MALI_TIMELINE_TRACKER_PP == tracker->type) {
355 if (mali_pp_job_is_virtual((struct mali_pp_job *)tracker->job)) {
356 _mali_osk_atomic_inc(&virt_pp_tracker_count);
358 _mali_osk_atomic_inc(&phy_pp_tracker_count);
362 /* Add tracker as new head on timeline's tracker list. */
363 if (NULL == timeline->tracker_head) {
364 /* Tracker list is empty. */
365 MALI_DEBUG_ASSERT(NULL == timeline->tracker_tail);
367 timeline->tracker_tail = tracker;
369 MALI_DEBUG_ASSERT(NULL == tracker->timeline_next);
370 MALI_DEBUG_ASSERT(NULL == tracker->timeline_prev);
372 MALI_DEBUG_ASSERT(NULL == timeline->tracker_head->timeline_next);
374 tracker->timeline_prev = timeline->tracker_head;
375 timeline->tracker_head->timeline_next = tracker;
377 MALI_DEBUG_ASSERT(NULL == tracker->timeline_next);
379 timeline->tracker_head = tracker;
381 MALI_DEBUG_ASSERT(NULL == timeline->tracker_head->timeline_next);
382 MALI_DEBUG_ASSERT(NULL == timeline->tracker_tail->timeline_prev);
385 /* Inserting the waiter object into the given timeline */
386 static void mali_timeline_insert_waiter(struct mali_timeline *timeline, struct mali_timeline_waiter *waiter_new)
388 struct mali_timeline_waiter *waiter_prev;
389 struct mali_timeline_waiter *waiter_next;
391 /* Waiter time must be between timeline head and tail, and there must
392 * be less than MALI_TIMELINE_MAX_POINT_SPAN elements between */
393 MALI_DEBUG_ASSERT((waiter_new->point - timeline->point_oldest) < MALI_TIMELINE_MAX_POINT_SPAN);
394 MALI_DEBUG_ASSERT((-waiter_new->point + timeline->point_next) < MALI_TIMELINE_MAX_POINT_SPAN);
396 /* Finding out where to put this waiter, in the linked waiter list of the given timeline **/
397 waiter_prev = timeline->waiter_head; /* Insert new after waiter_prev */
398 waiter_next = NULL; /* Insert new before waiter_next */
400 /* Iterating backwards from head (newest) to tail (oldest) until we
401 * find the correct spot to insert the new waiter */
402 while (waiter_prev && mali_timeline_point_after(waiter_prev->point, waiter_new->point)) {
403 waiter_next = waiter_prev;
404 waiter_prev = waiter_prev->timeline_prev;
407 if (NULL == waiter_prev && NULL == waiter_next) {
409 timeline->waiter_head = waiter_new;
410 timeline->waiter_tail = waiter_new;
411 } else if (NULL == waiter_next) {
413 waiter_new->timeline_prev = timeline->waiter_head;
414 timeline->waiter_head->timeline_next = waiter_new;
415 timeline->waiter_head = waiter_new;
416 } else if (NULL == waiter_prev) {
418 waiter_new->timeline_next = timeline->waiter_tail;
419 timeline->waiter_tail->timeline_prev = waiter_new;
420 timeline->waiter_tail = waiter_new;
423 waiter_new->timeline_next = waiter_next;
424 waiter_new->timeline_prev = waiter_prev;
425 waiter_next->timeline_prev = waiter_new;
426 waiter_prev->timeline_next = waiter_new;
430 static void mali_timeline_update_delayed_work(struct mali_timeline *timeline)
432 struct mali_timeline_system *system;
433 struct mali_timeline_tracker *oldest_tracker;
435 MALI_DEBUG_ASSERT_POINTER(timeline);
436 MALI_DEBUG_ASSERT(MALI_TIMELINE_SOFT == timeline->id);
438 system = timeline->system;
439 MALI_DEBUG_ASSERT_POINTER(system);
441 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
443 /* Timer is disabled, early out. */
444 if (!system->timer_enabled) return;
446 oldest_tracker = timeline->tracker_tail;
447 if (NULL != oldest_tracker && 0 == oldest_tracker->trigger_ref_count) {
448 if (MALI_FALSE == oldest_tracker->timer_active) {
449 if (MALI_TRUE == timeline->timer_active) {
450 _mali_osk_wq_delayed_cancel_work_async(timeline->delayed_work);
452 _mali_osk_wq_delayed_schedule_work(timeline->delayed_work, MALI_TIMELINE_TIMEOUT_HZ);
453 oldest_tracker->timer_active = MALI_TRUE;
454 timeline->timer_active = MALI_TRUE;
456 } else if (MALI_TRUE == timeline->timer_active) {
457 _mali_osk_wq_delayed_cancel_work_async(timeline->delayed_work);
458 timeline->timer_active = MALI_FALSE;
462 static mali_scheduler_mask mali_timeline_update_oldest_point(struct mali_timeline *timeline)
464 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
466 MALI_DEBUG_ASSERT_POINTER(timeline);
469 struct mali_timeline_system *system = timeline->system;
470 MALI_DEBUG_ASSERT_POINTER(system);
472 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
475 if (NULL != timeline->tracker_tail) {
476 /* Set oldest point to oldest tracker's point */
477 timeline->point_oldest = timeline->tracker_tail->point;
479 /* No trackers, mark point list as empty */
480 timeline->point_oldest = timeline->point_next;
483 /* Release all waiters no longer on the timeline's point list.
484 * Releasing a waiter can trigger this function to be called again, so
485 * we do not store any pointers on stack. */
486 while (NULL != timeline->waiter_tail) {
487 u32 waiter_time_relative;
488 u32 time_head_relative;
489 struct mali_timeline_waiter *waiter = timeline->waiter_tail;
491 time_head_relative = timeline->point_next - timeline->point_oldest;
492 waiter_time_relative = waiter->point - timeline->point_oldest;
494 if (waiter_time_relative < time_head_relative) {
495 /* This and all following waiters are on the point list, so we are done. */
499 /* Remove waiter from timeline's waiter list. */
500 if (NULL != waiter->timeline_next) {
501 waiter->timeline_next->timeline_prev = NULL;
503 /* This was the last waiter */
504 timeline->waiter_head = NULL;
506 timeline->waiter_tail = waiter->timeline_next;
508 /* Release waiter. This could activate a tracker, if this was
509 * the last waiter for the tracker. */
510 schedule_mask |= mali_timeline_system_release_waiter(timeline->system, waiter);
513 return schedule_mask;
516 void mali_timeline_tracker_init(struct mali_timeline_tracker *tracker,
517 mali_timeline_tracker_type type,
518 struct mali_timeline_fence *fence,
521 MALI_DEBUG_ASSERT_POINTER(tracker);
522 MALI_DEBUG_ASSERT_POINTER(job);
524 MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAX > type);
526 /* Zero out all tracker members. */
527 _mali_osk_memset(tracker, 0, sizeof(*tracker));
529 tracker->type = type;
531 tracker->trigger_ref_count = 1; /* Prevents any callback from trigging while adding it */
532 tracker->os_tick_create = _mali_osk_time_tickcount();
533 MALI_DEBUG_CODE(tracker->magic = MALI_TIMELINE_TRACKER_MAGIC);
535 tracker->activation_error = MALI_TIMELINE_ACTIVATION_ERROR_NONE;
539 _mali_osk_memcpy(&tracker->fence, fence, sizeof(struct mali_timeline_fence));
543 mali_scheduler_mask mali_timeline_tracker_release(struct mali_timeline_tracker *tracker)
545 struct mali_timeline *timeline;
546 struct mali_timeline_system *system;
547 struct mali_timeline_tracker *tracker_next, *tracker_prev;
548 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
549 u32 tid = _mali_osk_get_tid();
551 /* Upon entry a group lock will be held, but not a scheduler lock. */
552 MALI_DEBUG_ASSERT_POINTER(tracker);
553 MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAGIC == tracker->magic);
555 /* Tracker should have been triggered */
556 MALI_DEBUG_ASSERT(0 == tracker->trigger_ref_count);
558 /* All waiters should have been released at this point */
559 MALI_DEBUG_ASSERT(NULL == tracker->waiter_head);
560 MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail);
562 MALI_DEBUG_PRINT(3, ("Mali Timeline: releasing tracker for job 0x%08X\n", tracker->job));
564 timeline = tracker->timeline;
565 if (NULL == timeline) {
566 /* Tracker was not on a timeline, there is nothing to release. */
567 return MALI_SCHEDULER_MASK_EMPTY;
570 system = timeline->system;
571 MALI_DEBUG_ASSERT_POINTER(system);
573 mali_spinlock_reentrant_wait(system->spinlock, tid);
575 /* Tracker should still be on timeline */
576 MALI_DEBUG_ASSERT(!mali_timeline_is_empty(timeline));
577 MALI_DEBUG_ASSERT(mali_timeline_is_point_on(timeline, tracker->point));
579 /* Tracker is no longer valid. */
580 MALI_DEBUG_CODE(tracker->magic = 0);
582 tracker_next = tracker->timeline_next;
583 tracker_prev = tracker->timeline_prev;
584 tracker->timeline_next = NULL;
585 tracker->timeline_prev = NULL;
587 /* Removing tracker from timeline's tracker list */
588 if (NULL == tracker_next) {
589 /* This tracker was the head */
590 timeline->tracker_head = tracker_prev;
592 tracker_next->timeline_prev = tracker_prev;
595 if (NULL == tracker_prev) {
596 /* This tracker was the tail */
597 timeline->tracker_tail = tracker_next;
598 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
599 /* Update the timeline's oldest time and release any waiters */
600 schedule_mask |= mali_timeline_update_oldest_point(timeline);
601 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
603 tracker_prev->timeline_next = tracker_next;
606 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
608 /* Update delayed work only when it is the soft job timeline */
609 if (MALI_TIMELINE_SOFT == tracker->timeline->id) {
610 mali_timeline_update_delayed_work(tracker->timeline);
613 mali_spinlock_reentrant_signal(system->spinlock, tid);
615 return schedule_mask;
618 void mali_timeline_system_release_waiter_list(struct mali_timeline_system *system,
619 struct mali_timeline_waiter *tail,
620 struct mali_timeline_waiter *head)
622 MALI_DEBUG_ASSERT_POINTER(system);
623 MALI_DEBUG_ASSERT_POINTER(head);
624 MALI_DEBUG_ASSERT_POINTER(tail);
625 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
627 head->tracker_next = system->waiter_empty_list;
628 system->waiter_empty_list = tail;
631 static mali_scheduler_mask mali_timeline_tracker_activate(struct mali_timeline_tracker *tracker)
633 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
634 struct mali_timeline_system *system;
635 struct mali_timeline *timeline;
636 u32 tid = _mali_osk_get_tid();
638 MALI_DEBUG_ASSERT_POINTER(tracker);
639 MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAGIC == tracker->magic);
641 system = tracker->system;
642 MALI_DEBUG_ASSERT_POINTER(system);
643 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
645 tracker->os_tick_activate = _mali_osk_time_tickcount();
647 if (NULL != tracker->waiter_head) {
648 mali_timeline_system_release_waiter_list(system, tracker->waiter_tail, tracker->waiter_head);
649 tracker->waiter_head = NULL;
650 tracker->waiter_tail = NULL;
653 switch (tracker->type) {
654 case MALI_TIMELINE_TRACKER_GP:
655 schedule_mask = mali_scheduler_activate_gp_job((struct mali_gp_job *) tracker->job);
657 _mali_osk_atomic_dec(&gp_tracker_count);
659 case MALI_TIMELINE_TRACKER_PP:
660 if (mali_pp_job_is_virtual((struct mali_pp_job *)tracker->job)) {
661 _mali_osk_atomic_dec(&virt_pp_tracker_count);
663 _mali_osk_atomic_dec(&phy_pp_tracker_count);
665 schedule_mask = mali_scheduler_activate_pp_job((struct mali_pp_job *) tracker->job);
667 case MALI_TIMELINE_TRACKER_SOFT:
668 timeline = tracker->timeline;
669 MALI_DEBUG_ASSERT_POINTER(timeline);
671 schedule_mask |= mali_soft_job_system_activate_job((struct mali_soft_job *) tracker->job);
673 /* Start a soft timer to make sure the soft job be released in a limited time */
674 mali_spinlock_reentrant_wait(system->spinlock, tid);
675 mali_timeline_update_delayed_work(timeline);
676 mali_spinlock_reentrant_signal(system->spinlock, tid);
678 case MALI_TIMELINE_TRACKER_WAIT:
679 mali_timeline_fence_wait_activate((struct mali_timeline_fence_wait_tracker *) tracker->job);
681 case MALI_TIMELINE_TRACKER_SYNC:
682 #if defined(CONFIG_SYNC)
683 mali_timeline_sync_fence_activate((struct mali_timeline_sync_fence_tracker *) tracker->job);
685 MALI_PRINT_ERROR(("Mali Timeline: sync tracker not supported\n", tracker->type));
686 #endif /* defined(CONFIG_SYNC) */
689 MALI_PRINT_ERROR(("Mali Timeline - Illegal tracker type: %d\n", tracker->type));
693 return schedule_mask;
696 void mali_timeline_system_tracker_get(struct mali_timeline_system *system, struct mali_timeline_tracker *tracker)
698 u32 tid = _mali_osk_get_tid();
700 MALI_DEBUG_ASSERT_POINTER(tracker);
701 MALI_DEBUG_ASSERT_POINTER(system);
703 mali_spinlock_reentrant_wait(system->spinlock, tid);
705 MALI_DEBUG_ASSERT(0 < tracker->trigger_ref_count);
706 tracker->trigger_ref_count++;
708 mali_spinlock_reentrant_signal(system->spinlock, tid);
711 mali_scheduler_mask mali_timeline_system_tracker_put(struct mali_timeline_system *system, struct mali_timeline_tracker *tracker, mali_timeline_activation_error activation_error)
713 u32 tid = _mali_osk_get_tid();
714 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
716 MALI_DEBUG_ASSERT_POINTER(tracker);
717 MALI_DEBUG_ASSERT_POINTER(system);
719 mali_spinlock_reentrant_wait(system->spinlock, tid);
721 MALI_DEBUG_ASSERT(0 < tracker->trigger_ref_count);
722 tracker->trigger_ref_count--;
724 tracker->activation_error |= activation_error;
726 if (0 == tracker->trigger_ref_count) {
727 schedule_mask |= mali_timeline_tracker_activate(tracker);
731 mali_spinlock_reentrant_signal(system->spinlock, tid);
733 return schedule_mask;
736 void mali_timeline_fence_copy_uk_fence(struct mali_timeline_fence *fence, _mali_uk_fence_t *uk_fence)
740 MALI_DEBUG_ASSERT_POINTER(fence);
741 MALI_DEBUG_ASSERT_POINTER(uk_fence);
743 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
744 fence->points[i] = uk_fence->points[i];
747 fence->sync_fd = uk_fence->sync_fd;
750 struct mali_timeline_system *mali_timeline_system_create(struct mali_session_data *session)
753 struct mali_timeline_system *system;
755 MALI_DEBUG_ASSERT_POINTER(session);
756 MALI_DEBUG_PRINT(4, ("Mali Timeline: creating timeline system\n"));
758 system = (struct mali_timeline_system *) _mali_osk_calloc(1, sizeof(struct mali_timeline_system));
759 if (NULL == system) {
763 system->spinlock = mali_spinlock_reentrant_init(_MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM);
764 if (NULL == system->spinlock) {
765 mali_timeline_system_destroy(system);
769 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
770 system->timelines[i] = mali_timeline_create(system, (enum mali_timeline_id)i);
771 if (NULL == system->timelines[i]) {
772 mali_timeline_system_destroy(system);
777 #if defined(CONFIG_SYNC)
778 system->signaled_sync_tl = mali_sync_timeline_create(NULL, "mali-always-signaled");
779 if (NULL == system->signaled_sync_tl) {
780 mali_timeline_system_destroy(system);
783 #endif /* defined(CONFIG_SYNC) */
785 system->waiter_empty_list = NULL;
786 system->session = session;
787 system->timer_enabled = MALI_TRUE;
789 system->wait_queue = _mali_osk_wait_queue_init();
790 if (NULL == system->wait_queue) {
791 mali_timeline_system_destroy(system);
798 #if defined(CONFIG_MALI_DMA_BUF_FENCE) ||defined(CONFIG_SYNC)
800 * Check if there are any trackers left on timeline.
802 * Used as a wait queue conditional.
804 * @param data Timeline.
805 * @return MALI_TRUE if there are no trackers on timeline, MALI_FALSE if not.
807 static mali_bool mali_timeline_has_no_trackers(void *data)
809 struct mali_timeline *timeline = (struct mali_timeline *) data;
811 MALI_DEBUG_ASSERT_POINTER(timeline);
813 return mali_timeline_is_empty(timeline);
815 #if defined(CONFIG_SYNC)
817 * Cancel sync fence waiters waited upon by trackers on all timelines.
819 * Will return after all timelines have no trackers left.
821 * @param system Timeline system.
823 static void mali_timeline_cancel_sync_fence_waiters(struct mali_timeline_system *system)
826 u32 tid = _mali_osk_get_tid();
827 struct mali_timeline_tracker *tracker, *tracker_next;
828 _MALI_OSK_LIST_HEAD_STATIC_INIT(tracker_list);
830 MALI_DEBUG_ASSERT_POINTER(system);
831 MALI_DEBUG_ASSERT_POINTER(system->session);
832 MALI_DEBUG_ASSERT(system->session->is_aborting);
834 mali_spinlock_reentrant_wait(system->spinlock, tid);
836 /* Cancel sync fence waiters. */
837 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
838 struct mali_timeline *timeline = system->timelines[i];
840 MALI_DEBUG_ASSERT_POINTER(timeline);
842 tracker_next = timeline->tracker_tail;
843 while (NULL != tracker_next) {
844 tracker = tracker_next;
845 tracker_next = tracker->timeline_next;
847 if (NULL == tracker->sync_fence) continue;
849 MALI_DEBUG_PRINT(3, ("Mali Timeline: Cancelling sync fence wait for tracker 0x%08X.\n", tracker));
851 /* Cancel sync fence waiter. */
852 if (0 == sync_fence_cancel_async(tracker->sync_fence, &tracker->sync_fence_waiter)) {
853 /* Callback was not called, move tracker to local list. */
854 _mali_osk_list_add(&tracker->sync_fence_cancel_list, &tracker_list);
859 mali_spinlock_reentrant_signal(system->spinlock, tid);
861 /* Manually call sync fence callback in order to release waiter and trigger activation of tracker. */
862 _MALI_OSK_LIST_FOREACHENTRY(tracker, tracker_next, &tracker_list, struct mali_timeline_tracker, sync_fence_cancel_list) {
863 mali_timeline_sync_fence_callback(tracker->sync_fence, &tracker->sync_fence_waiter);
866 /* Sleep until all sync fence callbacks are done and all timelines are empty. */
867 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
868 struct mali_timeline *timeline = system->timelines[i];
870 MALI_DEBUG_ASSERT_POINTER(timeline);
872 _mali_osk_wait_queue_wait_event(system->wait_queue, mali_timeline_has_no_trackers, (void *) timeline);
876 #endif /* defined(CONFIG_SYNC) */
878 #if defined(CONFIG_MALI_DMA_BUF_FENCE)
879 static void mali_timeline_cancel_dma_fence_waiters(struct mali_timeline_system *system)
882 u32 tid = _mali_osk_get_tid();
883 struct mali_pp_job *pp_job = NULL;
884 struct mali_pp_job *next_pp_job = NULL;
885 struct mali_timeline *timeline = NULL;
886 struct mali_timeline_tracker *tracker, *tracker_next;
887 _MALI_OSK_LIST_HEAD_STATIC_INIT(pp_job_list);
889 MALI_DEBUG_ASSERT_POINTER(system);
890 MALI_DEBUG_ASSERT_POINTER(system->session);
891 MALI_DEBUG_ASSERT(system->session->is_aborting);
893 mali_spinlock_reentrant_wait(system->spinlock, tid);
895 /* Cancel dma fence waiters. */
896 timeline = system->timelines[MALI_TIMELINE_PP];
897 MALI_DEBUG_ASSERT_POINTER(timeline);
899 tracker_next = timeline->tracker_tail;
900 while (NULL != tracker_next) {
901 mali_bool fence_is_signaled = MALI_TRUE;
902 tracker = tracker_next;
903 tracker_next = tracker->timeline_next;
905 if (NULL == tracker->waiter_dma_fence) continue;
906 pp_job = (struct mali_pp_job *)tracker->job;
907 MALI_DEBUG_ASSERT_POINTER(pp_job);
908 MALI_DEBUG_PRINT(3, ("Mali Timeline: Cancelling dma fence waiter for tracker 0x%08X.\n", tracker));
910 for (j = 0; j < pp_job->dma_fence_context.num_dma_fence_waiter; j++) {
911 if (pp_job->dma_fence_context.mali_dma_fence_waiters[j]) {
912 /* Cancel a previously callback from the fence.
913 * This function returns true if the callback is successfully removed,
914 * or false if the fence has already been signaled.
916 bool ret = fence_remove_callback(pp_job->dma_fence_context.mali_dma_fence_waiters[j]->fence,
917 &pp_job->dma_fence_context.mali_dma_fence_waiters[j]->base);
919 fence_is_signaled = MALI_FALSE;
924 /* Callbacks were not called, move pp job to local list. */
925 if (MALI_FALSE == fence_is_signaled)
926 _mali_osk_list_add(&pp_job->list, &pp_job_list);
929 mali_spinlock_reentrant_signal(system->spinlock, tid);
931 /* Manually call dma fence callback in order to release waiter and trigger activation of tracker. */
932 _MALI_OSK_LIST_FOREACHENTRY(pp_job, next_pp_job, &pp_job_list, struct mali_pp_job, list) {
933 mali_timeline_dma_fence_callback((void *)pp_job);
936 /* Sleep until all dma fence callbacks are done and all timelines are empty. */
937 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
938 struct mali_timeline *timeline = system->timelines[i];
939 MALI_DEBUG_ASSERT_POINTER(timeline);
940 _mali_osk_wait_queue_wait_event(system->wait_queue, mali_timeline_has_no_trackers, (void *) timeline);
945 void mali_timeline_system_abort(struct mali_timeline_system *system)
947 MALI_DEBUG_CODE(u32 tid = _mali_osk_get_tid(););
949 MALI_DEBUG_ASSERT_POINTER(system);
950 MALI_DEBUG_ASSERT_POINTER(system->session);
951 MALI_DEBUG_ASSERT(system->session->is_aborting);
953 MALI_DEBUG_PRINT(3, ("Mali Timeline: Aborting timeline system for session 0x%08X.\n", system->session));
955 #if defined(CONFIG_SYNC)
956 mali_timeline_cancel_sync_fence_waiters(system);
957 #endif /* defined(CONFIG_SYNC) */
959 #if defined(CONFIG_MALI_DMA_BUF_FENCE)
960 mali_timeline_cancel_dma_fence_waiters(system);
963 /* Should not be any waiters or trackers left at this point. */
966 mali_spinlock_reentrant_wait(system->spinlock, tid);
967 for (i = 0; i < MALI_TIMELINE_MAX; ++i)
969 struct mali_timeline *timeline = system->timelines[i];
970 MALI_DEBUG_ASSERT_POINTER(timeline);
971 MALI_DEBUG_ASSERT(timeline->point_oldest == timeline->point_next);
972 MALI_DEBUG_ASSERT(NULL == timeline->tracker_head);
973 MALI_DEBUG_ASSERT(NULL == timeline->tracker_tail);
974 MALI_DEBUG_ASSERT(NULL == timeline->waiter_head);
975 MALI_DEBUG_ASSERT(NULL == timeline->waiter_tail);
977 mali_spinlock_reentrant_signal(system->spinlock, tid);
981 void mali_timeline_system_destroy(struct mali_timeline_system *system)
984 struct mali_timeline_waiter *waiter, *next;
985 #if defined(CONFIG_SYNC)
986 u32 tid = _mali_osk_get_tid();
989 MALI_DEBUG_ASSERT_POINTER(system);
990 MALI_DEBUG_ASSERT_POINTER(system->session);
992 MALI_DEBUG_PRINT(4, ("Mali Timeline: destroying timeline system\n"));
994 if (NULL != system) {
996 /* There should be no waiters left on this queue. */
997 if (NULL != system->wait_queue) {
998 _mali_osk_wait_queue_term(system->wait_queue);
999 system->wait_queue = NULL;
1002 /* Free all waiters in empty list */
1003 waiter = system->waiter_empty_list;
1004 while (NULL != waiter) {
1005 next = waiter->tracker_next;
1006 _mali_osk_free(waiter);
1010 #if defined(CONFIG_SYNC)
1011 if (NULL != system->signaled_sync_tl) {
1012 sync_timeline_destroy(system->signaled_sync_tl);
1015 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
1016 if ((NULL != system->timelines[i]) && (NULL != system->timelines[i]->spinlock)) {
1017 mali_spinlock_reentrant_wait(system->timelines[i]->spinlock, tid);
1018 system->timelines[i]->destroyed = MALI_TRUE;
1019 mali_spinlock_reentrant_signal(system->timelines[i]->spinlock, tid);
1022 #endif /* defined(CONFIG_SYNC) */
1024 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
1025 if (NULL != system->timelines[i]) {
1026 mali_timeline_destroy(system->timelines[i]);
1030 if (NULL != system->spinlock) {
1031 mali_spinlock_reentrant_term(system->spinlock);
1034 _mali_osk_free(system);
1039 * Find how many waiters are needed for a given fence.
1041 * @param fence The fence to check.
1042 * @return Number of waiters needed for fence.
1044 static u32 mali_timeline_fence_num_waiters(struct mali_timeline_fence *fence)
1046 u32 i, num_waiters = 0;
1048 MALI_DEBUG_ASSERT_POINTER(fence);
1050 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
1051 if (MALI_TIMELINE_NO_POINT != fence->points[i]) {
1056 #if defined(CONFIG_SYNC)
1057 if (-1 != fence->sync_fd) ++num_waiters;
1058 #endif /* defined(CONFIG_SYNC) */
1063 static struct mali_timeline_waiter *mali_timeline_system_get_zeroed_waiter(struct mali_timeline_system *system)
1065 struct mali_timeline_waiter *waiter;
1067 MALI_DEBUG_ASSERT_POINTER(system);
1068 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
1070 waiter = system->waiter_empty_list;
1071 if (NULL != waiter) {
1072 /* Remove waiter from empty list and zero it */
1073 system->waiter_empty_list = waiter->tracker_next;
1074 _mali_osk_memset(waiter, 0, sizeof(*waiter));
1077 /* Return NULL if list was empty. */
1081 static void mali_timeline_system_allocate_waiters(struct mali_timeline_system *system,
1082 struct mali_timeline_waiter **tail,
1083 struct mali_timeline_waiter **head,
1084 int max_num_waiters)
1086 u32 i, tid = _mali_osk_get_tid();
1088 struct mali_timeline_waiter *waiter;
1090 MALI_DEBUG_ASSERT_POINTER(system);
1091 MALI_DEBUG_ASSERT_POINTER(tail);
1092 MALI_DEBUG_ASSERT_POINTER(head);
1094 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
1096 *head = *tail = NULL;
1097 do_alloc = MALI_FALSE;
1099 while (i < max_num_waiters) {
1100 if (MALI_FALSE == do_alloc) {
1101 waiter = mali_timeline_system_get_zeroed_waiter(system);
1102 if (NULL == waiter) {
1103 do_alloc = MALI_TRUE;
1104 mali_spinlock_reentrant_signal(system->spinlock, tid);
1108 waiter = _mali_osk_calloc(1, sizeof(struct mali_timeline_waiter));
1109 if (NULL == waiter) break;
1112 if (NULL == *tail) {
1116 (*head)->tracker_next = waiter;
1120 if (MALI_TRUE == do_alloc) {
1121 mali_spinlock_reentrant_wait(system->spinlock, tid);
1126 * Create waiters for the given tracker. The tracker is activated when all waiters are release.
1128 * @note Tracker can potentially be activated before this function returns.
1130 * @param system Timeline system.
1131 * @param tracker Tracker we will create waiters for.
1132 * @param waiter_tail List of pre-allocated waiters.
1133 * @param waiter_head List of pre-allocated waiters.
1135 static void mali_timeline_system_create_waiters_and_unlock(struct mali_timeline_system *system,
1136 struct mali_timeline_tracker *tracker,
1137 struct mali_timeline_waiter *waiter_tail,
1138 struct mali_timeline_waiter *waiter_head)
1141 u32 tid = _mali_osk_get_tid();
1142 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
1143 #if defined(CONFIG_SYNC)
1144 struct sync_fence *sync_fence = NULL;
1145 #endif /* defined(CONFIG_SYNC) */
1147 MALI_DEBUG_ASSERT_POINTER(system);
1148 MALI_DEBUG_ASSERT_POINTER(tracker);
1150 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
1152 MALI_DEBUG_ASSERT(NULL == tracker->waiter_head);
1153 MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail);
1154 MALI_DEBUG_ASSERT(NULL != tracker->job);
1156 /* Creating waiter object for all the timelines the fence is put on. Inserting this waiter
1157 * into the timelines sorted list of waiters */
1158 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
1159 mali_timeline_point point;
1160 struct mali_timeline *timeline;
1161 struct mali_timeline_waiter *waiter;
1163 /* Get point on current timeline from tracker's fence. */
1164 point = tracker->fence.points[i];
1166 if (likely(MALI_TIMELINE_NO_POINT == point)) {
1167 /* Fence contains no point on this timeline so we don't need a waiter. */
1171 timeline = system->timelines[i];
1172 MALI_DEBUG_ASSERT_POINTER(timeline);
1174 if (unlikely(!mali_timeline_is_point_valid(timeline, point))) {
1175 MALI_PRINT_ERROR(("Mali Timeline: point %d is not valid (oldest=%d, next=%d)\n",
1176 point, timeline->point_oldest, timeline->point_next));
1180 if (likely(mali_timeline_is_point_released(timeline, point))) {
1181 /* Tracker representing the point has been released so we don't need a
1186 /* The point is on timeline. */
1187 MALI_DEBUG_ASSERT(mali_timeline_is_point_on(timeline, point));
1189 /* Get a new zeroed waiter object. */
1190 if (likely(NULL != waiter_tail)) {
1191 waiter = waiter_tail;
1192 waiter_tail = waiter_tail->tracker_next;
1194 MALI_PRINT_ERROR(("Mali Timeline: failed to allocate memory for waiter\n"));
1198 /* Yanking the trigger ref count of the tracker. */
1199 tracker->trigger_ref_count++;
1201 waiter->point = point;
1202 waiter->tracker = tracker;
1204 /* Insert waiter on tracker's singly-linked waiter list. */
1205 if (NULL == tracker->waiter_head) {
1207 MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail);
1208 tracker->waiter_tail = waiter;
1210 tracker->waiter_head->tracker_next = waiter;
1212 tracker->waiter_head = waiter;
1214 /* Add waiter to timeline. */
1215 mali_timeline_insert_waiter(timeline, waiter);
1217 #if defined(CONFIG_SYNC)
1218 if (-1 != tracker->fence.sync_fd) {
1220 struct mali_timeline_waiter *waiter;
1222 sync_fence = sync_fence_fdget(tracker->fence.sync_fd);
1223 if (unlikely(NULL == sync_fence)) {
1224 MALI_PRINT_ERROR(("Mali Timeline: failed to get sync fence from fd %d\n", tracker->fence.sync_fd));
1228 /* Check if we have a zeroed waiter object available. */
1229 if (unlikely(NULL == waiter_tail)) {
1230 MALI_PRINT_ERROR(("Mali Timeline: failed to allocate memory for waiter\n"));
1234 /* Start asynchronous wait that will release waiter when the fence is signaled. */
1235 sync_fence_waiter_init(&tracker->sync_fence_waiter, mali_timeline_sync_fence_callback);
1236 ret = sync_fence_wait_async(sync_fence, &tracker->sync_fence_waiter);
1238 /* Fence already signaled, no waiter needed. */
1239 tracker->fence.sync_fd = -1;
1241 } else if (0 != ret) {
1242 MALI_PRINT_ERROR(("Mali Timeline: sync fence fd %d signaled with error %d\n", tracker->fence.sync_fd, ret));
1243 tracker->activation_error |= MALI_TIMELINE_ACTIVATION_ERROR_SYNC_BIT;
1247 /* Grab new zeroed waiter object. */
1248 waiter = waiter_tail;
1249 waiter_tail = waiter_tail->tracker_next;
1251 /* Increase the trigger ref count of the tracker. */
1252 tracker->trigger_ref_count++;
1254 waiter->point = MALI_TIMELINE_NO_POINT;
1255 waiter->tracker = tracker;
1257 /* Insert waiter on tracker's singly-linked waiter list. */
1258 if (NULL == tracker->waiter_head) {
1260 MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail);
1261 tracker->waiter_tail = waiter;
1263 tracker->waiter_head->tracker_next = waiter;
1265 tracker->waiter_head = waiter;
1267 /* Also store waiter in separate field for easy access by sync callback. */
1268 tracker->waiter_sync = waiter;
1270 /* Store the sync fence in tracker so we can retrieve in abort session, if needed. */
1271 tracker->sync_fence = sync_fence;
1275 #endif /* defined(CONFIG_SYNC)*/
1276 #if defined(CONFIG_MALI_DMA_BUF_FENCE)
1277 if ((NULL != tracker->timeline) && (MALI_TIMELINE_PP == tracker->timeline->id)) {
1279 struct mali_pp_job *job = (struct mali_pp_job *)tracker->job;
1281 if (0 < job->dma_fence_context.num_dma_fence_waiter) {
1282 struct mali_timeline_waiter *waiter;
1283 /* Check if we have a zeroed waiter object available. */
1284 if (unlikely(NULL == waiter_tail)) {
1285 MALI_PRINT_ERROR(("Mali Timeline: failed to allocate memory for waiter\n"));
1289 /* Grab new zeroed waiter object. */
1290 waiter = waiter_tail;
1291 waiter_tail = waiter_tail->tracker_next;
1293 /* Increase the trigger ref count of the tracker. */
1294 tracker->trigger_ref_count++;
1296 waiter->point = MALI_TIMELINE_NO_POINT;
1297 waiter->tracker = tracker;
1299 /* Insert waiter on tracker's singly-linked waiter list. */
1300 if (NULL == tracker->waiter_head) {
1302 MALI_DEBUG_ASSERT(NULL == tracker->waiter_tail);
1303 tracker->waiter_tail = waiter;
1305 tracker->waiter_head->tracker_next = waiter;
1307 tracker->waiter_head = waiter;
1309 /* Also store waiter in separate field for easy access by sync callback. */
1310 tracker->waiter_dma_fence = waiter;
1313 #endif /* defined(CONFIG_MALI_DMA_BUF_FENCE)*/
1315 #if defined(CONFIG_MALI_DMA_BUF_FENCE) ||defined(CONFIG_SYNC)
1317 #endif /* defined(CONFIG_MALI_DMA_BUF_FENCE) || defined(CONFIG_SYNC) */
1319 if (NULL != waiter_tail) {
1320 mali_timeline_system_release_waiter_list(system, waiter_tail, waiter_head);
1323 /* Release the initial trigger ref count. */
1324 tracker->trigger_ref_count--;
1326 /* If there were no waiters added to this tracker we activate immediately. */
1327 if (0 == tracker->trigger_ref_count) {
1328 schedule_mask |= mali_timeline_tracker_activate(tracker);
1331 mali_spinlock_reentrant_signal(system->spinlock, tid);
1333 #if defined(CONFIG_SYNC)
1334 if (NULL != sync_fence) {
1335 sync_fence_put(sync_fence);
1337 #endif /* defined(CONFIG_SYNC) */
1339 mali_executor_schedule_from_mask(schedule_mask, MALI_FALSE);
1342 mali_timeline_point mali_timeline_system_add_tracker(struct mali_timeline_system *system,
1343 struct mali_timeline_tracker *tracker,
1344 enum mali_timeline_id timeline_id)
1346 int num_waiters = 0;
1347 struct mali_timeline_waiter *waiter_tail, *waiter_head;
1348 u32 tid = _mali_osk_get_tid();
1350 mali_timeline_point point = MALI_TIMELINE_NO_POINT;
1352 MALI_DEBUG_ASSERT_POINTER(system);
1353 MALI_DEBUG_ASSERT_POINTER(system->session);
1354 MALI_DEBUG_ASSERT_POINTER(tracker);
1356 MALI_DEBUG_ASSERT(MALI_FALSE == system->session->is_aborting);
1357 MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAX > tracker->type);
1358 MALI_DEBUG_ASSERT(MALI_TIMELINE_TRACKER_MAGIC == tracker->magic);
1360 MALI_DEBUG_PRINT(4, ("Mali Timeline: adding tracker for job %p, timeline: %d\n", tracker->job, timeline_id));
1362 MALI_DEBUG_ASSERT(0 < tracker->trigger_ref_count);
1363 tracker->system = system;
1365 mali_spinlock_reentrant_wait(system->spinlock, tid);
1367 num_waiters = mali_timeline_fence_num_waiters(&tracker->fence);
1369 #if defined(CONFIG_MALI_DMA_BUF_FENCE)
1370 if (MALI_TIMELINE_PP == timeline_id) {
1371 struct mali_pp_job *job = (struct mali_pp_job *)tracker->job;
1372 if (0 < job->dma_fence_context.num_dma_fence_waiter)
1377 /* Allocate waiters. */
1378 mali_timeline_system_allocate_waiters(system, &waiter_tail, &waiter_head, num_waiters);
1379 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
1381 /* Add tracker to timeline. This will allocate a point for the tracker on the timeline. If
1382 * timeline ID is MALI_TIMELINE_NONE the tracker will NOT be added to a timeline and the
1383 * point will be MALI_TIMELINE_NO_POINT.
1385 * NOTE: the tracker can fail to be added if the timeline is full. If this happens, the
1386 * point will be MALI_TIMELINE_NO_POINT. */
1387 MALI_DEBUG_ASSERT(timeline_id < MALI_TIMELINE_MAX || timeline_id == MALI_TIMELINE_NONE);
1388 if (likely(timeline_id < MALI_TIMELINE_MAX)) {
1389 struct mali_timeline *timeline = system->timelines[timeline_id];
1390 mali_timeline_insert_tracker(timeline, tracker);
1391 MALI_DEBUG_ASSERT(!mali_timeline_is_empty(timeline));
1394 point = tracker->point;
1396 /* Create waiters for tracker based on supplied fence. Each waiter will increase the
1397 * trigger ref count. */
1398 mali_timeline_system_create_waiters_and_unlock(system, tracker, waiter_tail, waiter_head);
1401 /* At this point the tracker object might have been freed so we should no longer
1405 /* The tracker will always be activated after calling add_tracker, even if NO_POINT is
1410 static mali_scheduler_mask mali_timeline_system_release_waiter(struct mali_timeline_system *system,
1411 struct mali_timeline_waiter *waiter)
1413 struct mali_timeline_tracker *tracker;
1414 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
1416 MALI_DEBUG_ASSERT_POINTER(system);
1417 MALI_DEBUG_ASSERT_POINTER(waiter);
1419 MALI_DEBUG_ASSERT(MALI_TIMELINE_SYSTEM_LOCKED(system));
1421 tracker = waiter->tracker;
1422 MALI_DEBUG_ASSERT_POINTER(tracker);
1424 /* At this point the waiter has been removed from the timeline's waiter list, but it is
1425 * still on the tracker's waiter list. All of the tracker's waiters will be released when
1426 * the tracker is activated. */
1428 waiter->point = MALI_TIMELINE_NO_POINT;
1429 waiter->tracker = NULL;
1431 tracker->trigger_ref_count--;
1432 if (0 == tracker->trigger_ref_count) {
1433 /* This was the last waiter; activate tracker */
1434 schedule_mask |= mali_timeline_tracker_activate(tracker);
1438 return schedule_mask;
1441 mali_timeline_point mali_timeline_system_get_latest_point(struct mali_timeline_system *system,
1442 enum mali_timeline_id timeline_id)
1444 mali_timeline_point point;
1445 struct mali_timeline *timeline;
1446 u32 tid = _mali_osk_get_tid();
1448 MALI_DEBUG_ASSERT_POINTER(system);
1450 if (MALI_TIMELINE_MAX <= timeline_id) {
1451 return MALI_TIMELINE_NO_POINT;
1454 mali_spinlock_reentrant_wait(system->spinlock, tid);
1456 timeline = system->timelines[timeline_id];
1457 MALI_DEBUG_ASSERT_POINTER(timeline);
1459 point = MALI_TIMELINE_NO_POINT;
1460 if (timeline->point_oldest != timeline->point_next) {
1461 point = timeline->point_next - 1;
1462 if (MALI_TIMELINE_NO_POINT == point) point--;
1465 mali_spinlock_reentrant_signal(system->spinlock, tid);
1470 void mali_timeline_initialize(void)
1472 _mali_osk_atomic_init(&gp_tracker_count, 0);
1473 _mali_osk_atomic_init(&phy_pp_tracker_count, 0);
1474 _mali_osk_atomic_init(&virt_pp_tracker_count, 0);
1477 void mali_timeline_terminate(void)
1479 _mali_osk_atomic_term(&gp_tracker_count);
1480 _mali_osk_atomic_term(&phy_pp_tracker_count);
1481 _mali_osk_atomic_term(&virt_pp_tracker_count);
1484 #if defined(MALI_TIMELINE_DEBUG_FUNCTIONS)
1486 static mali_bool is_waiting_on_timeline(struct mali_timeline_tracker *tracker, enum mali_timeline_id id)
1488 struct mali_timeline *timeline;
1489 struct mali_timeline_system *system;
1491 MALI_DEBUG_ASSERT_POINTER(tracker);
1493 MALI_DEBUG_ASSERT_POINTER(tracker->timeline);
1494 timeline = tracker->timeline;
1496 MALI_DEBUG_ASSERT_POINTER(timeline->system);
1497 system = timeline->system;
1499 if (MALI_TIMELINE_MAX > id) {
1500 if (MALI_TIMELINE_NO_POINT != tracker->fence.points[id]) {
1501 return mali_timeline_is_point_on(system->timelines[id], tracker->fence.points[id]);
1506 MALI_DEBUG_ASSERT(MALI_TIMELINE_NONE == id);
1511 static const char *timeline_id_to_string(enum mali_timeline_id id)
1514 case MALI_TIMELINE_GP:
1516 case MALI_TIMELINE_PP:
1518 case MALI_TIMELINE_SOFT:
1525 static const char *timeline_tracker_type_to_string(enum mali_timeline_tracker_type type)
1528 case MALI_TIMELINE_TRACKER_GP:
1530 case MALI_TIMELINE_TRACKER_PP:
1532 case MALI_TIMELINE_TRACKER_SOFT:
1534 case MALI_TIMELINE_TRACKER_WAIT:
1536 case MALI_TIMELINE_TRACKER_SYNC:
1543 mali_timeline_tracker_state mali_timeline_debug_get_tracker_state(struct mali_timeline_tracker *tracker)
1545 struct mali_timeline *timeline = NULL;
1547 MALI_DEBUG_ASSERT_POINTER(tracker);
1548 timeline = tracker->timeline;
1550 if (0 != tracker->trigger_ref_count) {
1551 return MALI_TIMELINE_TS_WAITING;
1554 if (timeline && (timeline->tracker_tail == tracker || NULL != tracker->timeline_prev)) {
1555 return MALI_TIMELINE_TS_ACTIVE;
1558 if (timeline && (MALI_TIMELINE_NO_POINT == tracker->point)) {
1559 return MALI_TIMELINE_TS_INIT;
1562 return MALI_TIMELINE_TS_FINISH;
1565 void mali_timeline_debug_print_tracker(struct mali_timeline_tracker *tracker, _mali_osk_print_ctx *print_ctx)
1567 const char *tracker_state = "IWAF";
1568 char state_char = 'I';
1569 char tracker_type[32] = {0};
1571 MALI_DEBUG_ASSERT_POINTER(tracker);
1573 state_char = *(tracker_state + mali_timeline_debug_get_tracker_state(tracker));
1574 _mali_osk_snprintf(tracker_type, sizeof(tracker_type), "%s", timeline_tracker_type_to_string(tracker->type));
1576 #if defined(CONFIG_SYNC)
1577 if (0 != tracker->trigger_ref_count) {
1578 _mali_osk_ctxprintf(print_ctx, "TL: %s %u %c - ref_wait:%u [%s(%u),%s(%u),%s(%u), fd:%d, fence:(0x%08X)] job:(0x%08X)\n",
1579 tracker_type, tracker->point, state_char, tracker->trigger_ref_count,
1580 is_waiting_on_timeline(tracker, MALI_TIMELINE_GP) ? "WaitGP" : " ", tracker->fence.points[0],
1581 is_waiting_on_timeline(tracker, MALI_TIMELINE_PP) ? "WaitPP" : " ", tracker->fence.points[1],
1582 is_waiting_on_timeline(tracker, MALI_TIMELINE_SOFT) ? "WaitSOFT" : " ", tracker->fence.points[2],
1583 tracker->fence.sync_fd, (unsigned int)(uintptr_t)(tracker->sync_fence), (unsigned int)(uintptr_t)(tracker->job));
1585 _mali_osk_ctxprintf(print_ctx, "TL: %s %u %c fd:%d fence:(0x%08X) job:(0x%08X)\n",
1586 tracker_type, tracker->point, state_char,
1587 tracker->fence.sync_fd, (unsigned int)(uintptr_t)(tracker->sync_fence), (unsigned int)(uintptr_t)(tracker->job));
1590 if (0 != tracker->trigger_ref_count) {
1591 _mali_osk_ctxprintf(print_ctx, "TL: %s %u %c - ref_wait:%u [%s(%u),%s(%u),%s(%u)] job:(0x%08X)\n",
1592 tracker_type, tracker->point, state_char, tracker->trigger_ref_count,
1593 is_waiting_on_timeline(tracker, MALI_TIMELINE_GP) ? "WaitGP" : " ", tracker->fence.points[0],
1594 is_waiting_on_timeline(tracker, MALI_TIMELINE_PP) ? "WaitPP" : " ", tracker->fence.points[1],
1595 is_waiting_on_timeline(tracker, MALI_TIMELINE_SOFT) ? "WaitSOFT" : " ", tracker->fence.points[2],
1596 (unsigned int)(uintptr_t)(tracker->job));
1598 _mali_osk_ctxprintf(print_ctx, "TL: %s %u %c job:(0x%08X)\n",
1599 tracker_type, tracker->point, state_char,
1600 (unsigned int)(uintptr_t)(tracker->job));
1605 void mali_timeline_debug_print_timeline(struct mali_timeline *timeline, _mali_osk_print_ctx *print_ctx)
1607 struct mali_timeline_tracker *tracker = NULL;
1609 MALI_DEBUG_ASSERT_POINTER(timeline);
1611 tracker = timeline->tracker_tail;
1612 while (NULL != tracker) {
1613 mali_timeline_debug_print_tracker(tracker, print_ctx);
1614 tracker = tracker->timeline_next;
1618 #if !(LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
1619 void mali_timeline_debug_direct_print_tracker(struct mali_timeline_tracker *tracker)
1621 const char *tracker_state = "IWAF";
1622 char state_char = 'I';
1623 char tracker_type[32] = {0};
1625 MALI_DEBUG_ASSERT_POINTER(tracker);
1627 state_char = *(tracker_state + mali_timeline_debug_get_tracker_state(tracker));
1628 _mali_osk_snprintf(tracker_type, sizeof(tracker_type), "%s", timeline_tracker_type_to_string(tracker->type));
1630 #if defined(CONFIG_SYNC)
1631 if (0 != tracker->trigger_ref_count) {
1632 MALI_PRINT(("TL: %s %u %c - ref_wait:%u [%s(%u),%s(%u),%s(%u), fd:%d, fence:(0x%08X)] job:(0x%08X)\n",
1633 tracker_type, tracker->point, state_char, tracker->trigger_ref_count,
1634 is_waiting_on_timeline(tracker, MALI_TIMELINE_GP) ? "WaitGP" : " ", tracker->fence.points[0],
1635 is_waiting_on_timeline(tracker, MALI_TIMELINE_PP) ? "WaitPP" : " ", tracker->fence.points[1],
1636 is_waiting_on_timeline(tracker, MALI_TIMELINE_SOFT) ? "WaitSOFT" : " ", tracker->fence.points[2],
1637 tracker->fence.sync_fd, tracker->sync_fence, tracker->job));
1639 MALI_PRINT(("TL: %s %u %c fd:%d fence:(0x%08X) job:(0x%08X)\n",
1640 tracker_type, tracker->point, state_char,
1641 tracker->fence.sync_fd, tracker->sync_fence, tracker->job));
1644 if (0 != tracker->trigger_ref_count) {
1645 MALI_PRINT(("TL: %s %u %c - ref_wait:%u [%s(%u),%s(%u),%s(%u)] job:(0x%08X)\n",
1646 tracker_type, tracker->point, state_char, tracker->trigger_ref_count,
1647 is_waiting_on_timeline(tracker, MALI_TIMELINE_GP) ? "WaitGP" : " ", tracker->fence.points[0],
1648 is_waiting_on_timeline(tracker, MALI_TIMELINE_PP) ? "WaitPP" : " ", tracker->fence.points[1],
1649 is_waiting_on_timeline(tracker, MALI_TIMELINE_SOFT) ? "WaitSOFT" : " ", tracker->fence.points[2],
1652 MALI_PRINT(("TL: %s %u %c job:(0x%08X)\n",
1653 tracker_type, tracker->point, state_char,
1659 void mali_timeline_debug_direct_print_timeline(struct mali_timeline *timeline)
1661 struct mali_timeline_tracker *tracker = NULL;
1663 MALI_DEBUG_ASSERT_POINTER(timeline);
1665 tracker = timeline->tracker_tail;
1666 while (NULL != tracker) {
1667 mali_timeline_debug_direct_print_tracker(tracker);
1668 tracker = tracker->timeline_next;
1674 void mali_timeline_debug_print_system(struct mali_timeline_system *system, _mali_osk_print_ctx *print_ctx)
1677 int num_printed = 0;
1678 u32 tid = _mali_osk_get_tid();
1680 MALI_DEBUG_ASSERT_POINTER(system);
1682 mali_spinlock_reentrant_wait(system->spinlock, tid);
1684 /* Print all timelines */
1685 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
1686 struct mali_timeline *timeline = system->timelines[i];
1688 MALI_DEBUG_ASSERT_POINTER(timeline);
1690 if (NULL == timeline->tracker_head) continue;
1692 _mali_osk_ctxprintf(print_ctx, "TL: Timeline %s:\n",
1693 timeline_id_to_string((enum mali_timeline_id)i));
1695 mali_timeline_debug_print_timeline(timeline, print_ctx);
1699 if (0 == num_printed) {
1700 _mali_osk_ctxprintf(print_ctx, "TL: All timelines empty\n");
1703 mali_spinlock_reentrant_signal(system->spinlock, tid);
1706 #endif /* defined(MALI_TIMELINE_DEBUG_FUNCTIONS) */
1708 #if defined(CONFIG_MALI_DMA_BUF_FENCE)
1709 void mali_timeline_dma_fence_callback(void *pp_job_ptr)
1711 struct mali_timeline_system *system;
1712 struct mali_timeline_waiter *waiter;
1713 struct mali_timeline_tracker *tracker;
1714 struct mali_pp_job *pp_job = (struct mali_pp_job *)pp_job_ptr;
1715 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
1716 u32 tid = _mali_osk_get_tid();
1717 mali_bool is_aborting = MALI_FALSE;
1719 MALI_DEBUG_ASSERT_POINTER(pp_job);
1721 tracker = &pp_job->tracker;
1722 MALI_DEBUG_ASSERT_POINTER(tracker);
1724 system = tracker->system;
1725 MALI_DEBUG_ASSERT_POINTER(system);
1726 MALI_DEBUG_ASSERT_POINTER(system->session);
1728 mali_spinlock_reentrant_wait(system->spinlock, tid);
1730 waiter = tracker->waiter_dma_fence;
1731 MALI_DEBUG_ASSERT_POINTER(waiter);
1733 schedule_mask |= mali_timeline_system_release_waiter(system, waiter);
1735 is_aborting = system->session->is_aborting;
1737 /* If aborting, wake up sleepers that are waiting for dma fence callbacks to complete. */
1739 _mali_osk_wait_queue_wake_up(system->wait_queue);
1742 mali_spinlock_reentrant_signal(system->spinlock, tid);
1745 mali_executor_schedule_from_mask(schedule_mask, MALI_TRUE);