xref: /OK3568_Linux_fs/kernel/drivers/gpu/arm/mali400/mali/linux/mali_osk_locks.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright (C) 2010-2014, 2016-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 
11 /**
12  * @file mali_osk_locks.c
13  * Implemenation of the OS abstraction layer for the kernel device driver
14  */
15 
16 #include "mali_osk_locks.h"
17 #include "mali_kernel_common.h"
18 #include "mali_osk.h"
19 
20 
21 #ifdef DEBUG
22 #ifdef LOCK_ORDER_CHECKING
23 static DEFINE_SPINLOCK(lock_tracking_lock);
24 static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid);
25 static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid);
26 static const char *const lock_order_to_string(_mali_osk_lock_order_t order);
27 #endif /* LOCK_ORDER_CHECKING */
28 
_mali_osk_locks_debug_init(struct _mali_osk_lock_debug_s * checker,_mali_osk_lock_flags_t flags,_mali_osk_lock_order_t order)29 void _mali_osk_locks_debug_init(struct _mali_osk_lock_debug_s *checker, _mali_osk_lock_flags_t flags, _mali_osk_lock_order_t order)
30 {
31 	checker->orig_flags = flags;
32 	checker->owner = 0;
33 
34 #ifdef LOCK_ORDER_CHECKING
35 	checker->order = order;
36 	checker->next = NULL;
37 #endif
38 }
39 
_mali_osk_locks_debug_add(struct _mali_osk_lock_debug_s * checker)40 void _mali_osk_locks_debug_add(struct _mali_osk_lock_debug_s *checker)
41 {
42 	checker->owner = _mali_osk_get_tid();
43 
44 #ifdef LOCK_ORDER_CHECKING
45 	if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) {
46 		if (!add_lock_to_log_and_check(checker, _mali_osk_get_tid())) {
47 			printk(KERN_ERR "%d: ERROR lock %p taken while holding a lock of a higher order.\n",
48 			       _mali_osk_get_tid(), checker);
49 			dump_stack();
50 		}
51 	}
52 #endif
53 }
54 
_mali_osk_locks_debug_remove(struct _mali_osk_lock_debug_s * checker)55 void _mali_osk_locks_debug_remove(struct _mali_osk_lock_debug_s *checker)
56 {
57 
58 #ifdef LOCK_ORDER_CHECKING
59 	if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) {
60 		remove_lock_from_log(checker, _mali_osk_get_tid());
61 	}
62 #endif
63 	checker->owner = 0;
64 }
65 
66 
67 #ifdef LOCK_ORDER_CHECKING
68 /* Lock order checking
69  * -------------------
70  *
71  * To assure that lock ordering scheme defined by _mali_osk_lock_order_t is strictly adhered to, the
72  * following function will, together with a linked list and some extra members in _mali_osk_lock_debug_s,
73  * make sure that a lock that is taken has a higher order than the current highest-order lock a
74  * thread holds.
75  *
76  * This is done in the following manner:
77  * - A linked list keeps track of locks held by a thread.
78  * - A `next' pointer is added to each lock. This is used to chain the locks together.
79  * - When taking a lock, the `add_lock_to_log_and_check' makes sure that taking
80  *   the given lock is legal. It will follow the linked list  to find the last
81  *   lock taken by this thread. If the last lock's order was lower than the
82  *   lock that is to be taken, it appends the new lock to the list and returns
83  *   true, if not, it return false. This return value is assert()'ed on in
84  *   _mali_osk_lock_wait().
85  */
86 
87 static struct _mali_osk_lock_debug_s *lock_lookup_list;
88 
dump_lock_tracking_list(void)89 static void dump_lock_tracking_list(void)
90 {
91 	struct _mali_osk_lock_debug_s *l;
92 	u32 n = 1;
93 
94 	/* print list for debugging purposes */
95 	l = lock_lookup_list;
96 
97 	while (NULL != l) {
98 		printk(" [lock: %p, tid_owner: %d, order: %d] ->", l, l->owner, l->order);
99 		l = l->next;
100 		MALI_DEBUG_ASSERT(n++ < 100);
101 	}
102 	printk(" NULL\n");
103 }
104 
tracking_list_length(void)105 static int tracking_list_length(void)
106 {
107 	struct _mali_osk_lock_debug_s *l;
108 	u32 n = 0;
109 	l = lock_lookup_list;
110 
111 	while (NULL != l) {
112 		l = l->next;
113 		n++;
114 		MALI_DEBUG_ASSERT(n < 100);
115 	}
116 	return n;
117 }
118 
add_lock_to_log_and_check(struct _mali_osk_lock_debug_s * lock,uint32_t tid)119 static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid)
120 {
121 	mali_bool ret = MALI_FALSE;
122 	_mali_osk_lock_order_t highest_order_for_tid = _MALI_OSK_LOCK_ORDER_FIRST;
123 	struct _mali_osk_lock_debug_s *highest_order_lock = (struct _mali_osk_lock_debug_s *)0xbeefbabe;
124 	struct _mali_osk_lock_debug_s *l;
125 	unsigned long local_lock_flag;
126 	u32 len;
127 
128 	spin_lock_irqsave(&lock_tracking_lock, local_lock_flag);
129 	len = tracking_list_length();
130 
131 	l  = lock_lookup_list;
132 	if (NULL == l) { /* This is the first lock taken by this thread -- record and return true */
133 		lock_lookup_list = lock;
134 		spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
135 		return MALI_TRUE;
136 	} else {
137 		/* Traverse the locks taken and find the lock of the highest order.
138 		 * Since several threads may hold locks, each lock's owner must be
139 		 * checked so that locks not owned by this thread can be ignored. */
140 		for (;;) {
141 			MALI_DEBUG_ASSERT_POINTER(l);
142 			if (tid == l->owner && l->order >= highest_order_for_tid) {
143 				highest_order_for_tid = l->order;
144 				highest_order_lock = l;
145 			}
146 
147 			if (NULL != l->next) {
148 				l = l->next;
149 			} else {
150 				break;
151 			}
152 		}
153 
154 		l->next = lock;
155 		l->next = NULL;
156 	}
157 
158 	/* We have now found the highest order lock currently held by this thread and can see if it is
159 	 * legal to take the requested lock. */
160 	ret = highest_order_for_tid < lock->order;
161 
162 	if (!ret) {
163 		printk(KERN_ERR "Took lock of order %d (%s) while holding lock of order %d (%s)\n",
164 		       lock->order, lock_order_to_string(lock->order),
165 		       highest_order_for_tid, lock_order_to_string(highest_order_for_tid));
166 		dump_lock_tracking_list();
167 	}
168 
169 	if (len + 1 != tracking_list_length()) {
170 		printk(KERN_ERR "************ lock: %p\n", lock);
171 		printk(KERN_ERR "************ before: %d *** after: %d ****\n", len, tracking_list_length());
172 		dump_lock_tracking_list();
173 		MALI_DEBUG_ASSERT_POINTER(NULL);
174 	}
175 
176 	spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
177 	return ret;
178 }
179 
remove_lock_from_log(struct _mali_osk_lock_debug_s * lock,uint32_t tid)180 static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid)
181 {
182 	struct _mali_osk_lock_debug_s *curr;
183 	struct _mali_osk_lock_debug_s *prev = NULL;
184 	unsigned long local_lock_flag;
185 	u32 len;
186 	u32 n = 0;
187 
188 	spin_lock_irqsave(&lock_tracking_lock, local_lock_flag);
189 	len = tracking_list_length();
190 	curr = lock_lookup_list;
191 
192 	if (NULL == curr) {
193 		printk(KERN_ERR "Error: Lock tracking list was empty on call to remove_lock_from_log\n");
194 		dump_lock_tracking_list();
195 	}
196 
197 	MALI_DEBUG_ASSERT_POINTER(curr);
198 
199 
200 	while (lock != curr) {
201 		prev = curr;
202 
203 		MALI_DEBUG_ASSERT_POINTER(curr);
204 		curr = curr->next;
205 		MALI_DEBUG_ASSERT(n++ < 100);
206 	}
207 
208 	if (NULL == prev) {
209 		lock_lookup_list = curr->next;
210 	} else {
211 		MALI_DEBUG_ASSERT_POINTER(curr);
212 		MALI_DEBUG_ASSERT_POINTER(prev);
213 		prev->next = curr->next;
214 	}
215 
216 	lock->next = NULL;
217 
218 	if (len - 1 != tracking_list_length()) {
219 		printk(KERN_ERR "************ lock: %p\n", lock);
220 		printk(KERN_ERR "************ before: %d *** after: %d ****\n", len, tracking_list_length());
221 		dump_lock_tracking_list();
222 		MALI_DEBUG_ASSERT_POINTER(NULL);
223 	}
224 
225 	spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
226 }
227 
lock_order_to_string(_mali_osk_lock_order_t order)228 static const char *const lock_order_to_string(_mali_osk_lock_order_t order)
229 {
230 	switch (order) {
231 	case _MALI_OSK_LOCK_ORDER_SESSIONS:
232 		return "_MALI_OSK_LOCK_ORDER_SESSIONS";
233 		break;
234 	case _MALI_OSK_LOCK_ORDER_MEM_SESSION:
235 		return "_MALI_OSK_LOCK_ORDER_MEM_SESSION";
236 		break;
237 	case _MALI_OSK_LOCK_ORDER_MEM_INFO:
238 		return "_MALI_OSK_LOCK_ORDER_MEM_INFO";
239 		break;
240 	case _MALI_OSK_LOCK_ORDER_MEM_PT_CACHE:
241 		return "_MALI_OSK_LOCK_ORDER_MEM_PT_CACHE";
242 		break;
243 	case _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP:
244 		return "_MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP";
245 		break;
246 	case _MALI_OSK_LOCK_ORDER_PM_EXECUTION:
247 		return "_MALI_OSK_LOCK_ORDER_PM_EXECUTION";
248 		break;
249 	case _MALI_OSK_LOCK_ORDER_EXECUTOR:
250 		return "_MALI_OSK_LOCK_ORDER_EXECUTOR";
251 		break;
252 	case _MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM:
253 		return "_MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM";
254 		break;
255 	case _MALI_OSK_LOCK_ORDER_SCHEDULER:
256 		return "_MALI_OSK_LOCK_ORDER_SCHEDULER";
257 		break;
258 	case _MALI_OSK_LOCK_ORDER_SCHEDULER_DEFERRED:
259 		return "_MALI_OSK_LOCK_ORDER_SCHEDULER_DEFERRED";
260 		break;
261 	case _MALI_OSK_LOCK_ORDER_DMA_COMMAND:
262 		return "_MALI_OSK_LOCK_ORDER_DMA_COMMAND";
263 		break;
264 	case _MALI_OSK_LOCK_ORDER_PROFILING:
265 		return "_MALI_OSK_LOCK_ORDER_PROFILING";
266 		break;
267 	case _MALI_OSK_LOCK_ORDER_L2:
268 		return "_MALI_OSK_LOCK_ORDER_L2";
269 		break;
270 	case _MALI_OSK_LOCK_ORDER_L2_COMMAND:
271 		return "_MALI_OSK_LOCK_ORDER_L2_COMMAND";
272 		break;
273 	case _MALI_OSK_LOCK_ORDER_UTILIZATION:
274 		return "_MALI_OSK_LOCK_ORDER_UTILIZATION";
275 		break;
276 	case _MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS:
277 		return "_MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS";
278 		break;
279 	case _MALI_OSK_LOCK_ORDER_PM_STATE:
280 		return "_MALI_OSK_LOCK_ORDER_PM_STATE";
281 		break;
282 	default:
283 		return "<UNKNOWN_LOCK_ORDER>";
284 	}
285 }
286 #endif /* LOCK_ORDER_CHECKING */
287 #endif /* DEBUG */
288