MALI: utgard: fix deadlocks when CONFIG_SYNC is set
authorchenzhen <chenzhen@rock-chips.com>
Fri, 2 Jun 2017 07:33:45 +0000 (15:33 +0800)
committerchenzhen <chenzhen@rock-chips.com>
Tue, 20 Jun 2017 08:03:23 +0000 (16:03 +0800)
This is the content of
Case693349_the_spinlock_fix_patch_2_for_kernel_4.4.patch
from support_mali, with slight modifications for building
with rockchip_linux_defconfig,
in which CONFIG_SYNC is not set.

Change-Id: Icedff21f7941fd1aefceb6be4fda638378fe4ca8
Signed-off-by: chenzhen <chenzhen@rock-chips.com>
drivers/gpu/arm/mali400/mali/common/mali_kernel_core.c
drivers/gpu/arm/mali400/mali/common/mali_timeline.c
drivers/gpu/arm/mali400/mali/common/mali_timeline.h

index f5c28ad41bfc4942402503b1a38c56389c652f32..7663b87fc17683254c0f1c049d73013bce94a23e 100755 (executable)
@@ -752,8 +752,12 @@ _mali_osk_errcode_t mali_initialize_subsystems(void)
 
        mali_pp_job_initialize();
 
-       mali_timeline_initialize();
-
+       err = mali_timeline_initialize();
+       if (_MALI_OSK_ERR_OK != err) {
+               mali_terminate_subsystems();
+               return err;
+       }
+       
        err = mali_session_initialize();
        if (_MALI_OSK_ERR_OK != err) {
                mali_terminate_subsystems();
index 5ff604b837f00c319e2ac45a380e6b01d8c1e3c6..662393ac881c4ca22d5f2cfe3da2168d1199b3e4 100755 (executable)
 
 #define MALI_TIMELINE_SYSTEM_LOCKED(system) (mali_spinlock_reentrant_is_held((system)->spinlock, _mali_osk_get_tid()))
 
+#if defined(CONFIG_SYNC)
+_mali_osk_wq_work_t *sync_fence_callback_work_t = NULL;
+_mali_osk_spinlock_irq_t *sync_fence_callback_list_lock = NULL;
+static _MALI_OSK_LIST_HEAD_STATIC_INIT(sync_fence_callback_queue);
+#endif
+
 /*
  * Following three elements are used to record how many
  * gp, physical pp or virtual pp jobs are delayed in the whole
@@ -70,84 +76,18 @@ static DECLARE_DELAYED_WORK(delayed_sync_fence_put, put_sync_fences);
 /* Callback that is called when a sync fence a tracker is waiting on is signaled. */
 static void mali_timeline_sync_fence_callback(struct sync_fence *sync_fence, struct sync_fence_waiter *sync_fence_waiter)
 {
-       struct mali_timeline_system  *system;
-       struct mali_timeline_waiter  *waiter;
        struct mali_timeline_tracker *tracker;
-       mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
-       u32 tid = _mali_osk_get_tid();
-       mali_bool is_aborting = MALI_FALSE;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
-       int fence_status = sync_fence->status;
-#else
-       int fence_status = atomic_read(&sync_fence->status);
-#endif
-
-       MALI_DEBUG_ASSERT_POINTER(sync_fence);
+       MALI_IGNORE(sync_fence);
        MALI_DEBUG_ASSERT_POINTER(sync_fence_waiter);
 
        tracker = _MALI_OSK_CONTAINER_OF(sync_fence_waiter, struct mali_timeline_tracker, sync_fence_waiter);
        MALI_DEBUG_ASSERT_POINTER(tracker);
 
-       system = tracker->system;
-       MALI_DEBUG_ASSERT_POINTER(system);
-       MALI_DEBUG_ASSERT_POINTER(system->session);
-
-       mali_spinlock_reentrant_wait(system->spinlock, tid);
-
-       is_aborting = system->session->is_aborting;
-       if (!is_aborting && (0 > fence_status)) {
-               MALI_PRINT_ERROR(("Mali Timeline: sync fence fd %d signaled with error %d\n", tracker->fence.sync_fd, fence_status));
-               tracker->activation_error |= MALI_TIMELINE_ACTIVATION_ERROR_SYNC_BIT;
-       }
-
-       waiter = tracker->waiter_sync;
-       MALI_DEBUG_ASSERT_POINTER(waiter);
-
-       tracker->sync_fence = NULL;
-       tracker->fence.sync_fd = -1;
-
-       schedule_mask |= mali_timeline_system_release_waiter(system, waiter);
-
-       /* If aborting, wake up sleepers that are waiting for sync fence callbacks to complete. */
-       if (is_aborting) {
-               _mali_osk_wait_queue_wake_up(system->wait_queue);
-       }
-
-       mali_spinlock_reentrant_signal(system->spinlock, tid);
-
-       /*
-        * Older versions of Linux, before 3.5, doesn't support fput() in interrupt
-        * context. For those older kernels, allocate a list object and put the
-        * fence object on that and defer the call to sync_fence_put() to a workqueue.
-        */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
-       {
-               struct mali_deferred_fence_put_entry *obj;
-
-               obj = kzalloc(sizeof(struct mali_deferred_fence_put_entry), GFP_ATOMIC);
-               if (obj) {
-                       unsigned long flags;
-                       mali_bool schedule = MALI_FALSE;
-
-                       obj->fence = sync_fence;
-
-                       spin_lock_irqsave(&mali_timeline_sync_fence_to_free_lock, flags);
-                       if (hlist_empty(&mali_timeline_sync_fence_to_free_list))
-                               schedule = MALI_TRUE;
-                       hlist_add_head(&obj->list, &mali_timeline_sync_fence_to_free_list);
-                       spin_unlock_irqrestore(&mali_timeline_sync_fence_to_free_lock, flags);
+       _mali_osk_spinlock_irq_lock(sync_fence_callback_list_lock);
+       _mali_osk_list_addtail(&tracker->sync_fence_signal_list, &sync_fence_callback_queue);
+       _mali_osk_spinlock_irq_unlock(sync_fence_callback_list_lock);
 
-                       if (schedule)
-                               schedule_delayed_work(&delayed_sync_fence_put, 0);
-               }
-       }
-#else
-       sync_fence_put(sync_fence);
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) */
-
-       if (!is_aborting) {
-               mali_executor_schedule_from_mask(schedule_mask, MALI_TRUE);
-       }
+       _mali_osk_wq_schedule_work(sync_fence_callback_work_t);
 }
 #endif /* defined(CONFIG_SYNC) */
 
@@ -1467,11 +1407,128 @@ mali_timeline_point mali_timeline_system_get_latest_point(struct mali_timeline_s
        return point;
 }
 
-void mali_timeline_initialize(void)
+#if defined(CONFIG_SYNC)
+static void mali_timeline_do_sync_fence_callback(void *arg)
+{
+       _MALI_OSK_LIST_HEAD_STATIC_INIT(list);
+       struct mali_timeline_tracker *tracker;
+       struct mali_timeline_tracker *tmp_tracker;
+       u32 tid = _mali_osk_get_tid();
+       
+       MALI_IGNORE(arg);
+
+       /*
+        * Quickly "unhook" the jobs pending to be deleted, so we can release
+        * the lock before we start deleting the job objects
+        * (without any locks held)
+        */
+       _mali_osk_spinlock_irq_lock(sync_fence_callback_list_lock);
+       _mali_osk_list_move_list(&sync_fence_callback_queue, &list);
+       _mali_osk_spinlock_irq_unlock(sync_fence_callback_list_lock);
+
+       _MALI_OSK_LIST_FOREACHENTRY(tracker, tmp_tracker, &list,
+                                   struct mali_timeline_tracker, sync_fence_signal_list) {
+               mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
+               mali_bool is_aborting = MALI_FALSE;
+               int fence_status = 0;
+               struct sync_fence *sync_fence = NULL;
+               struct mali_timeline_system  *system = NULL;
+               struct mali_timeline_waiter  *waiter = NULL;
+
+               _mali_osk_list_delinit(&tracker->sync_fence_signal_list);
+
+               sync_fence = tracker->sync_fence;
+               MALI_DEBUG_ASSERT_POINTER(sync_fence);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+               fence_status = sync_fence->status;
+#else
+               fence_status = atomic_read(&sync_fence->status);
+#endif
+
+               system = tracker->system;
+               MALI_DEBUG_ASSERT_POINTER(system);
+               MALI_DEBUG_ASSERT_POINTER(system->session);
+
+               mali_spinlock_reentrant_wait(system->spinlock, tid);
+
+               is_aborting = system->session->is_aborting;
+               if (!is_aborting && (0 > fence_status)) {
+                       MALI_PRINT_ERROR(("Mali Timeline: sync fence fd %d signaled with error %d\n", tracker->fence.sync_fd, fence_status));
+                       tracker->activation_error |= MALI_TIMELINE_ACTIVATION_ERROR_SYNC_BIT;
+               }
+
+               waiter = tracker->waiter_sync;
+               MALI_DEBUG_ASSERT_POINTER(waiter);
+
+               tracker->sync_fence = NULL;
+               tracker->fence.sync_fd = -1;
+
+               schedule_mask |= mali_timeline_system_release_waiter(system, waiter);
+
+               /* If aborting, wake up sleepers that are waiting for sync fence callbacks to complete. */
+               if (is_aborting) {
+                       _mali_osk_wait_queue_wake_up(system->wait_queue);
+               }
+
+               mali_spinlock_reentrant_signal(system->spinlock, tid);
+
+               /*
+                * Older versions of Linux, before 3.5, doesn't support fput() in interrupt
+                * context. For those older kernels, allocate a list object and put the
+                * fence object on that and defer the call to sync_fence_put() to a workqueue.
+                */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
+               {
+                       struct mali_deferred_fence_put_entry *obj;
+
+                       obj = kzalloc(sizeof(struct mali_deferred_fence_put_entry), GFP_ATOMIC);
+                       if (obj) {
+                               unsigned long flags;
+                               mali_bool schedule = MALI_FALSE;
+
+                               obj->fence = sync_fence;
+
+                               spin_lock_irqsave(&mali_timeline_sync_fence_to_free_lock, flags);
+                               if (hlist_empty(&mali_timeline_sync_fence_to_free_list))
+                                       schedule = MALI_TRUE;
+                               hlist_add_head(&obj->list, &mali_timeline_sync_fence_to_free_list);
+                               spin_unlock_irqrestore(&mali_timeline_sync_fence_to_free_lock, flags);
+
+                               if (schedule)
+                                       schedule_delayed_work(&delayed_sync_fence_put, 0);
+                       }
+               }
+#else
+               sync_fence_put(sync_fence);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) */
+
+               if (!is_aborting) {
+                       mali_executor_schedule_from_mask(schedule_mask, MALI_TRUE);
+               }
+       }
+}
+#endif /* defined(CONFIG_SYNC) */
+
+_mali_osk_errcode_t mali_timeline_initialize(void)
 {
        _mali_osk_atomic_init(&gp_tracker_count, 0);
        _mali_osk_atomic_init(&phy_pp_tracker_count, 0);
        _mali_osk_atomic_init(&virt_pp_tracker_count, 0);
+#if defined(CONFIG_SYNC)
+       sync_fence_callback_list_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_UNORDERED, _MALI_OSK_LOCK_ORDER_FIRST);
+       if (NULL == sync_fence_callback_list_lock) {
+               return _MALI_OSK_ERR_NOMEM;
+       }
+
+       sync_fence_callback_work_t = _mali_osk_wq_create_work(
+                                            mali_timeline_do_sync_fence_callback, NULL);
+
+       if (NULL == sync_fence_callback_work_t) {
+               return _MALI_OSK_ERR_FAULT;
+       }
+#endif
+       return _MALI_OSK_ERR_OK;
 }
 
 void mali_timeline_terminate(void)
@@ -1479,6 +1536,17 @@ void mali_timeline_terminate(void)
        _mali_osk_atomic_term(&gp_tracker_count);
        _mali_osk_atomic_term(&phy_pp_tracker_count);
        _mali_osk_atomic_term(&virt_pp_tracker_count);
+#if defined(CONFIG_SYNC)
+       if (NULL != sync_fence_callback_list_lock) {
+               _mali_osk_spinlock_irq_term(sync_fence_callback_list_lock);
+               sync_fence_callback_list_lock = NULL;
+       }
+
+       if (NULL != sync_fence_callback_work_t) {
+               _mali_osk_wq_delete_work(sync_fence_callback_work_t);
+               sync_fence_callback_work_t = NULL;
+       }
+#endif
 }
 
 #if defined(MALI_TIMELINE_DEBUG_FUNCTIONS)
index 60cf28096b7600bb72a210b2ae9bc50ca7502038..624761078572378f3e8d4b1f192baaa3524a0410 100755 (executable)
@@ -189,6 +189,7 @@ struct mali_timeline_tracker {
        struct sync_fence_waiter       sync_fence_waiter; /**< Used to connect sync fence and tracker in sync fence wait callback. */
        struct sync_fence             *sync_fence;   /**< The sync fence this tracker is waiting on. */
        _mali_osk_list_t               sync_fence_cancel_list; /**< List node used to cancel sync fence waiters. */
+       _mali_osk_list_t                sync_fence_signal_list; /** < List node used to singal sync fence callback function. */
 #endif /* defined(CONFIG_SYNC) */
 
 #if defined(CONFIG_MALI_DMA_BUF_FENCE)
@@ -466,7 +467,7 @@ MALI_STATIC_INLINE mali_bool mali_timeline_tracker_activation_error(
  */
 void mali_timeline_fence_copy_uk_fence(struct mali_timeline_fence *fence, _mali_uk_fence_t *uk_fence);
 
-void mali_timeline_initialize(void);
+_mali_osk_errcode_t mali_timeline_initialize(void);
 
 void mali_timeline_terminate(void);