1 /*
2 * Copyright (C) 2013-2014, 2017 ARM Limited. All rights reserved.
3 *
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.
6 *
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.
9 */
10 #include <linux/file.h>
11 #include "mali_timeline_fence_wait.h"
12 #include "mali_osk.h"
13 #include "mali_kernel_common.h"
14 #include "mali_spinlock_reentrant.h"
15
16 /**
17 * Allocate a fence waiter tracker.
18 *
19 * @return New fence waiter if successful, NULL if not.
20 */
mali_timeline_fence_wait_tracker_alloc(void)21 static struct mali_timeline_fence_wait_tracker *mali_timeline_fence_wait_tracker_alloc(void)
22 {
23 return (struct mali_timeline_fence_wait_tracker *) _mali_osk_calloc(1, sizeof(struct mali_timeline_fence_wait_tracker));
24 }
25
26 /**
27 * Free fence waiter tracker.
28 *
29 * @param wait Fence wait tracker to free.
30 */
mali_timeline_fence_wait_tracker_free(struct mali_timeline_fence_wait_tracker * wait)31 static void mali_timeline_fence_wait_tracker_free(struct mali_timeline_fence_wait_tracker *wait)
32 {
33 MALI_DEBUG_ASSERT_POINTER(wait);
34 _mali_osk_atomic_term(&wait->refcount);
35 _mali_osk_free(wait);
36 }
37
38 /**
39 * Check if fence wait tracker has been activated. Used as a wait queue condition.
40 *
41 * @param data Fence waiter.
42 * @return MALI_TRUE if tracker has been activated, MALI_FALSE if not.
43 */
mali_timeline_fence_wait_tracker_is_activated(void * data)44 static mali_bool mali_timeline_fence_wait_tracker_is_activated(void *data)
45 {
46 struct mali_timeline_fence_wait_tracker *wait;
47
48 wait = (struct mali_timeline_fence_wait_tracker *) data;
49 MALI_DEBUG_ASSERT_POINTER(wait);
50
51 return wait->activated;
52 }
53
54 /**
55 * Check if fence has been signaled.
56 *
57 * @param system Timeline system.
58 * @param fence Timeline fence.
59 * @return MALI_TRUE if fence is signaled, MALI_FALSE if not.
60 */
mali_timeline_fence_wait_check_status(struct mali_timeline_system * system,struct mali_timeline_fence * fence)61 static mali_bool mali_timeline_fence_wait_check_status(struct mali_timeline_system *system, struct mali_timeline_fence *fence)
62 {
63 int i;
64 u32 tid = _mali_osk_get_tid();
65 mali_bool ret = MALI_TRUE;
66 #if defined(CONFIG_SYNC) || defined(CONFIG_SYNC_FILE)
67 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)
68 struct sync_fence *sync_fence = NULL;
69 #else
70 struct mali_internal_sync_fence *sync_fence = NULL;
71 #endif
72 #endif
73
74 MALI_DEBUG_ASSERT_POINTER(system);
75 MALI_DEBUG_ASSERT_POINTER(fence);
76
77 mali_spinlock_reentrant_wait(system->spinlock, tid);
78
79 for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
80 struct mali_timeline *timeline;
81 mali_timeline_point point;
82
83 point = fence->points[i];
84
85 if (likely(MALI_TIMELINE_NO_POINT == point)) {
86 /* Fence contains no point on this timeline. */
87 continue;
88 }
89
90 timeline = system->timelines[i];
91 MALI_DEBUG_ASSERT_POINTER(timeline);
92
93 if (unlikely(!mali_timeline_is_point_valid(timeline, point))) {
94 MALI_PRINT_ERROR(("Mali Timeline: point %d is not valid (oldest=%d, next=%d)\n", point, timeline->point_oldest, timeline->point_next));
95 }
96
97 if (!mali_timeline_is_point_released(timeline, point)) {
98 ret = MALI_FALSE;
99 goto exit;
100 }
101 }
102
103 #if defined(CONFIG_SYNC) || defined(CONFIG_SYNC_FILE)
104 if (-1 != fence->sync_fd) {
105 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)
106 sync_fence = sync_fence_fdget(fence->sync_fd);
107 #else
108 sync_fence = mali_internal_sync_fence_fdget(fence->sync_fd);
109 #endif
110 if (likely(NULL != sync_fence)) {
111 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
112 if (0 == sync_fence->status) {
113 #elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
114 if (0 < atomic_read(&sync_fence->status)) {
115 #else
116 if (0 == sync_fence->fence->ops->signaled(sync_fence->fence)) {
117 #endif
118 ret = MALI_FALSE;
119
120 } else {
121 ret = MALI_TRUE;
122 }
123 } else {
124 MALI_PRINT_ERROR(("Mali Timeline: failed to get sync fence from fd %d\n", fence->sync_fd));
125 }
126 }
127 #endif /* defined(CONFIG_SYNC) || defined(CONFIG_SYNC_FILE) */
128
129 exit:
130 mali_spinlock_reentrant_signal(system->spinlock, tid);
131
132 #if defined(CONFIG_SYNC) || defined(CONFIG_SYNC_FILE)
133 if (NULL != sync_fence) {
134 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)
135 sync_fence_put(sync_fence);
136 #else
137 fput(sync_fence->file);
138 #endif
139 }
140 #endif /* defined(CONFIG_SYNC) || defined(CONFIG_SYNC_FILE) */
141
142 return ret;
143 }
144
145 mali_bool mali_timeline_fence_wait(struct mali_timeline_system *system, struct mali_timeline_fence *fence, u32 timeout)
146 {
147 struct mali_timeline_fence_wait_tracker *wait;
148 mali_timeline_point point;
149 mali_bool ret;
150
151 MALI_DEBUG_ASSERT_POINTER(system);
152 MALI_DEBUG_ASSERT_POINTER(fence);
153
154 MALI_DEBUG_PRINT(4, ("Mali Timeline: wait on fence\n"));
155
156 if (MALI_TIMELINE_FENCE_WAIT_TIMEOUT_IMMEDIATELY == timeout) {
157 return mali_timeline_fence_wait_check_status(system, fence);
158 }
159
160 wait = mali_timeline_fence_wait_tracker_alloc();
161 if (unlikely(NULL == wait)) {
162 MALI_PRINT_ERROR(("Mali Timeline: failed to allocate data for fence wait\n"));
163 return MALI_FALSE;
164 }
165
166 wait->activated = MALI_FALSE;
167 wait->system = system;
168
169 /* Initialize refcount to two references. The reference first will be released by this
170 * function after the wait is over. The second reference will be released when the tracker
171 * is activated. */
172 _mali_osk_atomic_init(&wait->refcount, 2);
173
174 /* Add tracker to timeline system, but not to a timeline. */
175 mali_timeline_tracker_init(&wait->tracker, MALI_TIMELINE_TRACKER_WAIT, fence, wait);
176 point = mali_timeline_system_add_tracker(system, &wait->tracker, MALI_TIMELINE_NONE);
177 MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT == point);
178 MALI_IGNORE(point);
179
180 /* Wait for the tracker to be activated or time out. */
181 if (MALI_TIMELINE_FENCE_WAIT_TIMEOUT_NEVER == timeout) {
182 _mali_osk_wait_queue_wait_event(system->wait_queue, mali_timeline_fence_wait_tracker_is_activated, (void *) wait);
183 } else {
184 _mali_osk_wait_queue_wait_event_timeout(system->wait_queue, mali_timeline_fence_wait_tracker_is_activated, (void *) wait, timeout);
185 }
186
187 ret = wait->activated;
188
189 if (0 == _mali_osk_atomic_dec_return(&wait->refcount)) {
190 mali_timeline_fence_wait_tracker_free(wait);
191 }
192
193 return ret;
194 }
195
196 void mali_timeline_fence_wait_activate(struct mali_timeline_fence_wait_tracker *wait)
197 {
198 mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
199
200 MALI_DEBUG_ASSERT_POINTER(wait);
201 MALI_DEBUG_ASSERT_POINTER(wait->system);
202
203 MALI_DEBUG_PRINT(4, ("Mali Timeline: activation for fence wait tracker\n"));
204
205 MALI_DEBUG_ASSERT(MALI_FALSE == wait->activated);
206 wait->activated = MALI_TRUE;
207
208 _mali_osk_wait_queue_wake_up(wait->system->wait_queue);
209
210 /* Nothing can wait on this tracker, so nothing to schedule after release. */
211 schedule_mask = mali_timeline_tracker_release(&wait->tracker);
212 MALI_DEBUG_ASSERT(MALI_SCHEDULER_MASK_EMPTY == schedule_mask);
213 MALI_IGNORE(schedule_mask);
214
215 if (0 == _mali_osk_atomic_dec_return(&wait->refcount)) {
216 mali_timeline_fence_wait_tracker_free(wait);
217 }
218 }
219