2 * Copyright (C) 2010-2014, 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.
12 * @file mali_osk_locks.c
13 * Implemenation of the OS abstraction layer for the kernel device driver
16 #include "mali_osk_locks.h"
17 #include "mali_kernel_common.h"
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 */
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)
31 checker->orig_flags = flags;
34 #ifdef LOCK_ORDER_CHECKING
35 checker->order = order;
40 void _mali_osk_locks_debug_add(struct _mali_osk_lock_debug_s *checker)
42 checker->owner = _mali_osk_get_tid();
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);
55 void _mali_osk_locks_debug_remove(struct _mali_osk_lock_debug_s *checker)
58 #ifdef LOCK_ORDER_CHECKING
59 if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) {
60 remove_lock_from_log(checker, _mali_osk_get_tid());
67 #ifdef LOCK_ORDER_CHECKING
68 /* Lock order checking
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
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().
87 static struct _mali_osk_lock_debug_s *lock_lookup_list;
89 static void dump_lock_tracking_list(void)
91 struct _mali_osk_lock_debug_s *l;
94 /* print list for debugging purposes */
98 printk(" [lock: %p, tid_owner: %d, order: %d] ->", l, l->owner, l->order);
100 MALI_DEBUG_ASSERT(n++ < 100);
105 static int tracking_list_length(void)
107 struct _mali_osk_lock_debug_s *l;
109 l = lock_lookup_list;
114 MALI_DEBUG_ASSERT(n < 100);
119 static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid)
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;
128 spin_lock_irqsave(&lock_tracking_lock, local_lock_flag);
129 len = tracking_list_length();
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);
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. */
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;
147 if (NULL != l->next) {
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;
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();
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);
176 spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
180 static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid)
182 struct _mali_osk_lock_debug_s *curr;
183 struct _mali_osk_lock_debug_s *prev = NULL;
184 unsigned long local_lock_flag;
188 spin_lock_irqsave(&lock_tracking_lock, local_lock_flag);
189 len = tracking_list_length();
190 curr = lock_lookup_list;
193 printk(KERN_ERR "Error: Lock tracking list was empty on call to remove_lock_from_log\n");
194 dump_lock_tracking_list();
197 MALI_DEBUG_ASSERT_POINTER(curr);
200 while (lock != curr) {
203 MALI_DEBUG_ASSERT_POINTER(curr);
205 MALI_DEBUG_ASSERT(n++ < 100);
209 lock_lookup_list = curr->next;
211 MALI_DEBUG_ASSERT_POINTER(curr);
212 MALI_DEBUG_ASSERT_POINTER(prev);
213 prev->next = curr->next;
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);
225 spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
228 static const char *const lock_order_to_string(_mali_osk_lock_order_t order)
231 case _MALI_OSK_LOCK_ORDER_SESSIONS:
232 return "_MALI_OSK_LOCK_ORDER_SESSIONS";
234 case _MALI_OSK_LOCK_ORDER_MEM_SESSION:
235 return "_MALI_OSK_LOCK_ORDER_MEM_SESSION";
237 case _MALI_OSK_LOCK_ORDER_MEM_INFO:
238 return "_MALI_OSK_LOCK_ORDER_MEM_INFO";
240 case _MALI_OSK_LOCK_ORDER_MEM_PT_CACHE:
241 return "_MALI_OSK_LOCK_ORDER_MEM_PT_CACHE";
243 case _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP:
244 return "_MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP";
246 case _MALI_OSK_LOCK_ORDER_PM_EXECUTION:
247 return "_MALI_OSK_LOCK_ORDER_PM_EXECUTION";
249 case _MALI_OSK_LOCK_ORDER_EXECUTOR:
250 return "_MALI_OSK_LOCK_ORDER_EXECUTOR";
252 case _MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM:
253 return "_MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM";
255 case _MALI_OSK_LOCK_ORDER_SCHEDULER:
256 return "_MALI_OSK_LOCK_ORDER_SCHEDULER";
258 case _MALI_OSK_LOCK_ORDER_SCHEDULER_DEFERRED:
259 return "_MALI_OSK_LOCK_ORDER_SCHEDULER_DEFERRED";
261 case _MALI_OSK_LOCK_ORDER_DMA_COMMAND:
262 return "_MALI_OSK_LOCK_ORDER_DMA_COMMAND";
264 case _MALI_OSK_LOCK_ORDER_PROFILING:
265 return "_MALI_OSK_LOCK_ORDER_PROFILING";
267 case _MALI_OSK_LOCK_ORDER_L2:
268 return "_MALI_OSK_LOCK_ORDER_L2";
270 case _MALI_OSK_LOCK_ORDER_L2_COMMAND:
271 return "_MALI_OSK_LOCK_ORDER_L2_COMMAND";
273 case _MALI_OSK_LOCK_ORDER_UTILIZATION:
274 return "_MALI_OSK_LOCK_ORDER_UTILIZATION";
276 case _MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS:
277 return "_MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS";
279 case _MALI_OSK_LOCK_ORDER_PM_STATE:
280 return "_MALI_OSK_LOCK_ORDER_PM_STATE";
283 return "<UNKNOWN_LOCK_ORDER>";
286 #endif /* LOCK_ORDER_CHECKING */