import PULS_20160108
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / misc / mediatek / gpu / mt8127 / mali / mali / linux / mali_osk_locks.c
diff --git a/drivers/misc/mediatek/gpu/mt8127/mali/mali/linux/mali_osk_locks.c b/drivers/misc/mediatek/gpu/mt8127/mali/mali/linux/mali_osk_locks.c
new file mode 100644 (file)
index 0000000..c4d8a23
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * This confidential and proprietary software may be used only as
+ * authorised by a licensing agreement from ARM Limited
+ * (C) COPYRIGHT 2008-2013 ARM Limited
+ * ALL RIGHTS RESERVED
+ * The entire notice above must be reproduced on all authorised
+ * copies and copies may only be made to the extent permitted
+ * by a licensing agreement from ARM Limited.
+ */
+
+/**
+ * @file mali_osk_locks.c
+ * Implemenation of the OS abstraction layer for the kernel device driver
+ */
+
+#include "mali_osk_locks.h"
+#include "mali_kernel_common.h"
+#include "mali_osk.h"
+
+
+#ifdef DEBUG
+#ifdef LOCK_ORDER_CHECKING
+static DEFINE_SPINLOCK(lock_tracking_lock);
+static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid);
+static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid);
+static const char * const lock_order_to_string(_mali_osk_lock_order_t order);
+#endif /* LOCK_ORDER_CHECKING */
+
+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)
+{
+       checker->orig_flags = flags;
+       checker->owner = 0;
+
+#ifdef LOCK_ORDER_CHECKING
+       checker->order = order;
+       checker->next = NULL;
+#endif
+}
+
+void _mali_osk_locks_debug_add(struct _mali_osk_lock_debug_s *checker)
+{
+       checker->owner = _mali_osk_get_tid();
+
+#ifdef LOCK_ORDER_CHECKING
+       if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) {
+               if (!add_lock_to_log_and_check(checker, _mali_osk_get_tid())) {
+                       printk(KERN_ERR "%d: ERROR lock %p taken while holding a lock of a higher order.\n",
+                              _mali_osk_get_tid(), checker);
+                       dump_stack();
+               }
+       }
+#endif
+}
+
+void _mali_osk_locks_debug_remove(struct _mali_osk_lock_debug_s *checker)
+{
+
+#ifdef LOCK_ORDER_CHECKING
+       if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) {
+               remove_lock_from_log(checker, _mali_osk_get_tid());
+       }
+#endif
+       checker->owner = 0;
+}
+
+
+#ifdef LOCK_ORDER_CHECKING
+/* Lock order checking
+ * -------------------
+ *
+ * To assure that lock ordering scheme defined by _mali_osk_lock_order_t is strictly adhered to, the
+ * following function will, together with a linked list and some extra members in _mali_osk_lock_debug_s,
+ * make sure that a lock that is taken has a higher order than the current highest-order lock a
+ * thread holds.
+ *
+ * This is done in the following manner:
+ * - A linked list keeps track of locks held by a thread.
+ * - A `next' pointer is added to each lock. This is used to chain the locks together.
+ * - When taking a lock, the `add_lock_to_log_and_check' makes sure that taking
+ *   the given lock is legal. It will follow the linked list  to find the last
+ *   lock taken by this thread. If the last lock's order was lower than the
+ *   lock that is to be taken, it appends the new lock to the list and returns
+ *   true, if not, it return false. This return value is assert()'ed on in
+ *   _mali_osk_lock_wait().
+ */
+
+static struct _mali_osk_lock_debug_s *lock_lookup_list;
+
+static void dump_lock_tracking_list(void)
+{
+       struct _mali_osk_lock_debug_s *l;
+       u32 n = 1;
+
+       /* print list for debugging purposes */
+       l = lock_lookup_list;
+
+       while (NULL != l) {
+               printk(" [lock: %p, tid_owner: %d, order: %d] ->", l, l->owner, l->order);
+               l = l->next;
+               MALI_DEBUG_ASSERT(n++ < 100);
+       }
+       printk(" NULL\n");
+}
+
+static int tracking_list_length(void)
+{
+       struct _mali_osk_lock_debug_s *l;
+       u32 n = 0;
+       l = lock_lookup_list;
+
+       while (NULL != l) {
+               l = l->next;
+               n++;
+               MALI_DEBUG_ASSERT(n < 100);
+       }
+       return n;
+}
+
+static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid)
+{
+       mali_bool ret = MALI_FALSE;
+       _mali_osk_lock_order_t highest_order_for_tid = _MALI_OSK_LOCK_ORDER_FIRST;
+       struct _mali_osk_lock_debug_s *highest_order_lock = (struct _mali_osk_lock_debug_s *)0xbeefbabe;
+       struct _mali_osk_lock_debug_s *l;
+       unsigned long local_lock_flag;
+       u32 len;
+
+       spin_lock_irqsave(&lock_tracking_lock, local_lock_flag);
+       len = tracking_list_length();
+
+       l  = lock_lookup_list;
+       if (NULL == l) { /* This is the first lock taken by this thread -- record and return true */
+               lock_lookup_list = lock;
+               spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
+               return MALI_TRUE;
+       } else {
+               /* Traverse the locks taken and find the lock of the highest order.
+                * Since several threads may hold locks, each lock's owner must be
+                * checked so that locks not owned by this thread can be ignored. */
+               for(;;) {
+                       MALI_DEBUG_ASSERT_POINTER( l );
+                       if (tid == l->owner && l->order >= highest_order_for_tid) {
+                               highest_order_for_tid = l->order;
+                               highest_order_lock = l;
+                       }
+
+                       if (NULL != l->next) {
+                               l = l->next;
+                       } else {
+                               break;
+                       }
+               }
+
+               l->next = lock;
+               l->next = NULL;
+       }
+
+       /* We have now found the highest order lock currently held by this thread and can see if it is
+        * legal to take the requested lock. */
+       ret = highest_order_for_tid < lock->order;
+
+       if (!ret) {
+               printk(KERN_ERR "Took lock of order %d (%s) while holding lock of order %d (%s)\n",
+                      lock->order, lock_order_to_string(lock->order),
+                      highest_order_for_tid, lock_order_to_string(highest_order_for_tid));
+               dump_lock_tracking_list();
+       }
+
+       if (len+1 != tracking_list_length()) {
+               printk(KERN_ERR "************ lock: %p\n", lock);
+               printk(KERN_ERR "************ before: %d *** after: %d ****\n", len, tracking_list_length());
+               dump_lock_tracking_list();
+               MALI_DEBUG_ASSERT_POINTER(NULL);
+       }
+
+       spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
+       return ret;
+}
+
+static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid)
+{
+       struct _mali_osk_lock_debug_s *curr;
+       struct _mali_osk_lock_debug_s *prev = NULL;
+       unsigned long local_lock_flag;
+       u32 len;
+       u32 n = 0;
+
+       spin_lock_irqsave(&lock_tracking_lock, local_lock_flag);
+       len = tracking_list_length();
+       curr = lock_lookup_list;
+
+       if (NULL == curr) {
+               printk(KERN_ERR "Error: Lock tracking list was empty on call to remove_lock_from_log\n");
+               dump_lock_tracking_list();
+       }
+
+       MALI_DEBUG_ASSERT_POINTER(curr);
+
+
+       while (lock != curr) {
+               prev = curr;
+
+               MALI_DEBUG_ASSERT_POINTER(curr);
+               curr = curr->next;
+               MALI_DEBUG_ASSERT(n++ < 100);
+       }
+
+       if (NULL == prev) {
+               lock_lookup_list = curr->next;
+       } else {
+               MALI_DEBUG_ASSERT_POINTER(curr);
+               MALI_DEBUG_ASSERT_POINTER(prev);
+               prev->next = curr->next;
+       }
+
+       lock->next = NULL;
+
+       if (len-1 != tracking_list_length()) {
+               printk(KERN_ERR "************ lock: %p\n", lock);
+               printk(KERN_ERR "************ before: %d *** after: %d ****\n", len, tracking_list_length());
+               dump_lock_tracking_list();
+               MALI_DEBUG_ASSERT_POINTER(NULL);
+       }
+
+       spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
+}
+
+static const char * const lock_order_to_string(_mali_osk_lock_order_t order)
+{
+       switch (order) {
+       case _MALI_OSK_LOCK_ORDER_SESSIONS:
+               return "_MALI_OSK_LOCK_ORDER_SESSIONS";
+               break;
+       case _MALI_OSK_LOCK_ORDER_MEM_SESSION:
+               return "_MALI_OSK_LOCK_ORDER_MEM_SESSION";
+               break;
+       case _MALI_OSK_LOCK_ORDER_MEM_INFO:
+               return "_MALI_OSK_LOCK_ORDER_MEM_INFO";
+               break;
+       case _MALI_OSK_LOCK_ORDER_MEM_PT_CACHE:
+               return "_MALI_OSK_LOCK_ORDER_MEM_PT_CACHE";
+               break;
+       case _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP:
+               return "_MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP";
+               break;
+       case _MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL:
+               return "_MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL";
+               break;
+       case _MALI_OSK_LOCK_ORDER_GROUP:
+               return "_MALI_OSK_LOCK_ORDER_GROUP";
+               break;
+       case _MALI_OSK_LOCK_ORDER_SCHEDULER:
+               return "_MALI_OSK_LOCK_ORDER_SCHEDULER";
+               break;
+       case _MALI_OSK_LOCK_ORDER_PM_CORE_STATE:
+               return "_MALI_OSK_LOCK_ORDER_PM_CORE_STATE";
+               break;
+       case _MALI_OSK_LOCK_ORDER_L2_COMMAND:
+               return "_MALI_OSK_LOCK_ORDER_L2_COMMAND";
+               break;
+       case _MALI_OSK_LOCK_ORDER_PROFILING:
+               return "_MALI_OSK_LOCK_ORDER_PROFILING";
+               break;
+       case _MALI_OSK_LOCK_ORDER_L2_COUNTER:
+               return "_MALI_OSK_LOCK_ORDER_L2_COUNTER";
+               break;
+       case _MALI_OSK_LOCK_ORDER_UTILIZATION:
+               return "_MALI_OSK_LOCK_ORDER_UTILIZATION";
+               break;
+       case _MALI_OSK_LOCK_ORDER_PM_EXECUTE:
+               return "_MALI_OSK_LOCK_ORDER_PM_EXECUTE";
+               break;
+       case _MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS:
+               return "_MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS";
+               break;
+       default:
+               return "";
+       }
+}
+#endif /* LOCK_ORDER_CHECKING */
+#endif /* DEBUG */