From f2605eb08a6b1dc06791fde4853c43c241b863f0 Mon Sep 17 00:00:00 2001 From: chenzhen Date: Fri, 2 Jun 2017 15:33:45 +0800 Subject: [PATCH] MALI: utgard: fix deadlocks when CONFIG_SYNC is set 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 --- .../mali400/mali/common/mali_kernel_core.c | 8 +- .../arm/mali400/mali/common/mali_timeline.c | 212 ++++++++++++------ .../arm/mali400/mali/common/mali_timeline.h | 3 +- 3 files changed, 148 insertions(+), 75 deletions(-) diff --git a/drivers/gpu/arm/mali400/mali/common/mali_kernel_core.c b/drivers/gpu/arm/mali400/mali/common/mali_kernel_core.c index f5c28ad41bfc..7663b87fc176 100755 --- a/drivers/gpu/arm/mali400/mali/common/mali_kernel_core.c +++ b/drivers/gpu/arm/mali400/mali/common/mali_kernel_core.c @@ -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(); diff --git a/drivers/gpu/arm/mali400/mali/common/mali_timeline.c b/drivers/gpu/arm/mali400/mali/common/mali_timeline.c index 5ff604b837f0..662393ac881c 100755 --- a/drivers/gpu/arm/mali400/mali/common/mali_timeline.c +++ b/drivers/gpu/arm/mali400/mali/common/mali_timeline.c @@ -19,6 +19,12 @@ #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) diff --git a/drivers/gpu/arm/mali400/mali/common/mali_timeline.h b/drivers/gpu/arm/mali400/mali/common/mali_timeline.h index 60cf28096b76..624761078572 100755 --- a/drivers/gpu/arm/mali400/mali/common/mali_timeline.h +++ b/drivers/gpu/arm/mali400/mali/common/mali_timeline.h @@ -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); -- 2.34.1