| /* |
| * Copyright (C) 2013-2014, 2016 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. |
| * |
| * A copy of the licence is included with the program, and can also be obtained from Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "mali_soft_job.h" |
| #include "mali_osk.h" |
| #include "mali_timeline.h" |
| #include "mali_session.h" |
| #include "mali_kernel_common.h" |
| #include "mali_uk_types.h" |
| #include "mali_scheduler.h" |
| #include "mali_executor.h" |
| |
| MALI_STATIC_INLINE void mali_soft_job_system_lock(struct mali_soft_job_system *system) |
| { |
| MALI_DEBUG_ASSERT_POINTER(system); |
| _mali_osk_spinlock_irq_lock(system->lock); |
| MALI_DEBUG_PRINT(5, ("Mali Soft Job: soft system %p lock taken\n", system)); |
| MALI_DEBUG_ASSERT(0 == system->lock_owner); |
| MALI_DEBUG_CODE(system->lock_owner = _mali_osk_get_tid()); |
| } |
| |
| MALI_STATIC_INLINE void mali_soft_job_system_unlock(struct mali_soft_job_system *system) |
| { |
| MALI_DEBUG_ASSERT_POINTER(system); |
| MALI_DEBUG_PRINT(5, ("Mali Soft Job: releasing soft system %p lock\n", system)); |
| MALI_DEBUG_ASSERT(_mali_osk_get_tid() == system->lock_owner); |
| MALI_DEBUG_CODE(system->lock_owner = 0); |
| _mali_osk_spinlock_irq_unlock(system->lock); |
| } |
| |
| #if defined(DEBUG) |
| MALI_STATIC_INLINE void mali_soft_job_system_assert_locked(struct mali_soft_job_system *system) |
| { |
| MALI_DEBUG_ASSERT_POINTER(system); |
| MALI_DEBUG_ASSERT(_mali_osk_get_tid() == system->lock_owner); |
| } |
| #define MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system) mali_soft_job_system_assert_locked(system) |
| #else |
| #define MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system) |
| #endif /* defined(DEBUG) */ |
| |
| struct mali_soft_job_system *mali_soft_job_system_create(struct mali_session_data *session) |
| { |
| struct mali_soft_job_system *system; |
| |
| MALI_DEBUG_ASSERT_POINTER(session); |
| |
| system = (struct mali_soft_job_system *) _mali_osk_calloc(1, sizeof(struct mali_soft_job_system)); |
| if (NULL == system) { |
| return NULL; |
| } |
| |
| system->session = session; |
| |
| system->lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER); |
| if (NULL == system->lock) { |
| mali_soft_job_system_destroy(system); |
| return NULL; |
| } |
| system->lock_owner = 0; |
| system->last_job_id = 0; |
| |
| _MALI_OSK_INIT_LIST_HEAD(&(system->jobs_used)); |
| |
| return system; |
| } |
| |
| void mali_soft_job_system_destroy(struct mali_soft_job_system *system) |
| { |
| MALI_DEBUG_ASSERT_POINTER(system); |
| |
| /* All jobs should be free at this point. */ |
| MALI_DEBUG_ASSERT(_mali_osk_list_empty(&(system->jobs_used))); |
| |
| if (NULL != system) { |
| if (NULL != system->lock) { |
| _mali_osk_spinlock_irq_term(system->lock); |
| } |
| _mali_osk_free(system); |
| } |
| } |
| |
| static void mali_soft_job_system_free_job(struct mali_soft_job_system *system, struct mali_soft_job *job) |
| { |
| MALI_DEBUG_ASSERT_POINTER(job); |
| MALI_DEBUG_ASSERT_POINTER(system); |
| |
| mali_soft_job_system_lock(job->system); |
| |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_INVALID_ID != job->id); |
| MALI_DEBUG_ASSERT(system == job->system); |
| |
| _mali_osk_list_del(&(job->system_list)); |
| |
| mali_soft_job_system_unlock(job->system); |
| |
| _mali_osk_free(job); |
| } |
| |
| MALI_STATIC_INLINE struct mali_soft_job *mali_soft_job_system_lookup_job(struct mali_soft_job_system *system, u32 job_id) |
| { |
| struct mali_soft_job *job, *tmp; |
| |
| MALI_DEBUG_ASSERT_POINTER(system); |
| MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system); |
| |
| _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &system->jobs_used, struct mali_soft_job, system_list) { |
| if (job->id == job_id) |
| return job; |
| } |
| |
| return NULL; |
| } |
| |
| void mali_soft_job_destroy(struct mali_soft_job *job) |
| { |
| MALI_DEBUG_ASSERT_POINTER(job); |
| MALI_DEBUG_ASSERT_POINTER(job->system); |
| |
| MALI_DEBUG_PRINT(4, ("Mali Soft Job: destroying soft job %u (0x%08X)\n", job->id, job)); |
| |
| if (NULL != job) { |
| if (0 < _mali_osk_atomic_dec_return(&job->refcount)) return; |
| |
| _mali_osk_atomic_term(&job->refcount); |
| |
| if (NULL != job->activated_notification) { |
| _mali_osk_notification_delete(job->activated_notification); |
| job->activated_notification = NULL; |
| } |
| |
| mali_soft_job_system_free_job(job->system, job); |
| } |
| } |
| |
| struct mali_soft_job *mali_soft_job_create(struct mali_soft_job_system *system, mali_soft_job_type type, u64 user_job) |
| { |
| struct mali_soft_job *job; |
| _mali_osk_notification_t *notification = NULL; |
| |
| MALI_DEBUG_ASSERT_POINTER(system); |
| MALI_DEBUG_ASSERT((MALI_SOFT_JOB_TYPE_USER_SIGNALED == type) || |
| (MALI_SOFT_JOB_TYPE_SELF_SIGNALED == type)); |
| |
| notification = _mali_osk_notification_create(_MALI_NOTIFICATION_SOFT_ACTIVATED, sizeof(_mali_uk_soft_job_activated_s)); |
| if (unlikely(NULL == notification)) { |
| MALI_PRINT_ERROR(("Mali Soft Job: failed to allocate notification")); |
| return NULL; |
| } |
| |
| job = _mali_osk_malloc(sizeof(struct mali_soft_job)); |
| if (unlikely(NULL == job)) { |
| MALI_DEBUG_PRINT(2, ("Mali Soft Job: system alloc job failed. \n")); |
| return NULL; |
| } |
| |
| mali_soft_job_system_lock(system); |
| |
| job->system = system; |
| job->id = system->last_job_id++; |
| job->state = MALI_SOFT_JOB_STATE_ALLOCATED; |
| |
| _mali_osk_list_add(&(job->system_list), &(system->jobs_used)); |
| |
| job->type = type; |
| job->user_job = user_job; |
| job->activated = MALI_FALSE; |
| |
| job->activated_notification = notification; |
| |
| _mali_osk_atomic_init(&job->refcount, 1); |
| |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_ALLOCATED == job->state); |
| MALI_DEBUG_ASSERT(system == job->system); |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_INVALID_ID != job->id); |
| |
| mali_soft_job_system_unlock(system); |
| |
| return job; |
| } |
| |
| mali_timeline_point mali_soft_job_start(struct mali_soft_job *job, struct mali_timeline_fence *fence) |
| { |
| mali_timeline_point point; |
| struct mali_soft_job_system *system; |
| |
| MALI_DEBUG_ASSERT_POINTER(job); |
| MALI_DEBUG_ASSERT_POINTER(fence); |
| |
| MALI_DEBUG_ASSERT_POINTER(job->system); |
| system = job->system; |
| |
| MALI_DEBUG_ASSERT_POINTER(system->session); |
| MALI_DEBUG_ASSERT_POINTER(system->session->timeline_system); |
| |
| mali_soft_job_system_lock(system); |
| |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_ALLOCATED == job->state); |
| job->state = MALI_SOFT_JOB_STATE_STARTED; |
| |
| mali_soft_job_system_unlock(system); |
| |
| MALI_DEBUG_PRINT(4, ("Mali Soft Job: starting soft job %u (0x%08X)\n", job->id, job)); |
| |
| mali_timeline_tracker_init(&job->tracker, MALI_TIMELINE_TRACKER_SOFT, fence, job); |
| point = mali_timeline_system_add_tracker(system->session->timeline_system, &job->tracker, MALI_TIMELINE_SOFT); |
| |
| return point; |
| } |
| |
| static mali_bool mali_soft_job_is_activated(void *data) |
| { |
| struct mali_soft_job *job; |
| |
| job = (struct mali_soft_job *) data; |
| MALI_DEBUG_ASSERT_POINTER(job); |
| |
| return job->activated; |
| } |
| |
| _mali_osk_errcode_t mali_soft_job_system_signal_job(struct mali_soft_job_system *system, u32 job_id) |
| { |
| struct mali_soft_job *job; |
| struct mali_timeline_system *timeline_system; |
| mali_scheduler_mask schedule_mask; |
| |
| MALI_DEBUG_ASSERT_POINTER(system); |
| |
| mali_soft_job_system_lock(system); |
| |
| job = mali_soft_job_system_lookup_job(system, job_id); |
| |
| if ((NULL == job) || (MALI_SOFT_JOB_TYPE_USER_SIGNALED != job->type) |
| || !(MALI_SOFT_JOB_STATE_STARTED == job->state || MALI_SOFT_JOB_STATE_TIMED_OUT == job->state)) { |
| mali_soft_job_system_unlock(system); |
| MALI_PRINT_ERROR(("Mali Soft Job: invalid soft job id %u", job_id)); |
| return _MALI_OSK_ERR_ITEM_NOT_FOUND; |
| } |
| |
| if (MALI_SOFT_JOB_STATE_TIMED_OUT == job->state) { |
| job->state = MALI_SOFT_JOB_STATE_SIGNALED; |
| mali_soft_job_system_unlock(system); |
| |
| MALI_DEBUG_ASSERT(MALI_TRUE == job->activated); |
| MALI_DEBUG_PRINT(4, ("Mali Soft Job: soft job %u (0x%08X) was timed out\n", job->id, job)); |
| mali_soft_job_destroy(job); |
| |
| return _MALI_OSK_ERR_TIMEOUT; |
| } |
| |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state); |
| |
| job->state = MALI_SOFT_JOB_STATE_SIGNALED; |
| mali_soft_job_system_unlock(system); |
| |
| /* Since the job now is in signaled state, timeouts from the timeline system will be |
| * ignored, and it is not possible to signal this job again. */ |
| |
| timeline_system = system->session->timeline_system; |
| MALI_DEBUG_ASSERT_POINTER(timeline_system); |
| |
| /* Wait until activated. */ |
| _mali_osk_wait_queue_wait_event(timeline_system->wait_queue, mali_soft_job_is_activated, (void *) job); |
| |
| MALI_DEBUG_PRINT(4, ("Mali Soft Job: signaling soft job %u (0x%08X)\n", job->id, job)); |
| |
| schedule_mask = mali_timeline_tracker_release(&job->tracker); |
| mali_executor_schedule_from_mask(schedule_mask, MALI_FALSE); |
| |
| mali_soft_job_destroy(job); |
| |
| return _MALI_OSK_ERR_OK; |
| } |
| |
| static void mali_soft_job_send_activated_notification(struct mali_soft_job *job) |
| { |
| if (NULL != job->activated_notification) { |
| _mali_uk_soft_job_activated_s *res = job->activated_notification->result_buffer; |
| res->user_job = job->user_job; |
| mali_session_send_notification(job->system->session, job->activated_notification); |
| } |
| job->activated_notification = NULL; |
| } |
| |
| mali_scheduler_mask mali_soft_job_system_activate_job(struct mali_soft_job *job) |
| { |
| mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; |
| |
| MALI_DEBUG_ASSERT_POINTER(job); |
| MALI_DEBUG_ASSERT_POINTER(job->system); |
| MALI_DEBUG_ASSERT_POINTER(job->system->session); |
| |
| MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeline activation for soft job %u (0x%08X).\n", job->id, job)); |
| |
| mali_soft_job_system_lock(job->system); |
| |
| if (unlikely(job->system->session->is_aborting)) { |
| MALI_DEBUG_PRINT(3, ("Mali Soft Job: Soft job %u (0x%08X) activated while session is aborting.\n", job->id, job)); |
| |
| mali_soft_job_system_unlock(job->system); |
| |
| /* Since we are in shutdown, we can ignore the scheduling bitmask. */ |
| mali_timeline_tracker_release(&job->tracker); |
| mali_soft_job_destroy(job); |
| return schedule_mask; |
| } |
| |
| /* Send activated notification. */ |
| mali_soft_job_send_activated_notification(job); |
| |
| /* Wake up sleeping signaler. */ |
| job->activated = MALI_TRUE; |
| |
| /* If job type is self signaled, release tracker, move soft job to free list, and scheduler at once */ |
| if (MALI_SOFT_JOB_TYPE_SELF_SIGNALED == job->type) { |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state); |
| |
| job->state = MALI_SOFT_JOB_STATE_SIGNALED; |
| mali_soft_job_system_unlock(job->system); |
| |
| schedule_mask |= mali_timeline_tracker_release(&job->tracker); |
| |
| mali_soft_job_destroy(job); |
| } else { |
| _mali_osk_wait_queue_wake_up(job->tracker.system->wait_queue); |
| |
| mali_soft_job_system_unlock(job->system); |
| } |
| |
| return schedule_mask; |
| } |
| |
| mali_scheduler_mask mali_soft_job_system_timeout_job(struct mali_soft_job *job) |
| { |
| mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY; |
| |
| MALI_DEBUG_ASSERT_POINTER(job); |
| MALI_DEBUG_ASSERT_POINTER(job->system); |
| MALI_DEBUG_ASSERT_POINTER(job->system->session); |
| MALI_DEBUG_ASSERT(MALI_TRUE == job->activated); |
| |
| MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeline timeout for soft job %u (0x%08X).\n", job->id, job)); |
| |
| mali_soft_job_system_lock(job->system); |
| |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state || |
| MALI_SOFT_JOB_STATE_SIGNALED == job->state); |
| |
| if (unlikely(job->system->session->is_aborting)) { |
| /* The session is aborting. This job will be released and destroyed by @ref |
| * mali_soft_job_system_abort(). */ |
| mali_soft_job_system_unlock(job->system); |
| |
| return MALI_SCHEDULER_MASK_EMPTY; |
| } |
| |
| if (MALI_SOFT_JOB_STATE_STARTED != job->state) { |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_SIGNALED == job->state); |
| |
| /* The job is about to be signaled, ignore timeout. */ |
| MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeout on soft job %u (0x%08X) in signaled state.\n", job->id, job)); |
| mali_soft_job_system_unlock(job->system); |
| return schedule_mask; |
| } |
| |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state); |
| |
| job->state = MALI_SOFT_JOB_STATE_TIMED_OUT; |
| _mali_osk_atomic_inc(&job->refcount); |
| |
| mali_soft_job_system_unlock(job->system); |
| |
| schedule_mask = mali_timeline_tracker_release(&job->tracker); |
| |
| mali_soft_job_destroy(job); |
| |
| return schedule_mask; |
| } |
| |
| void mali_soft_job_system_abort(struct mali_soft_job_system *system) |
| { |
| struct mali_soft_job *job, *tmp; |
| _MALI_OSK_LIST_HEAD_STATIC_INIT(jobs); |
| |
| MALI_DEBUG_ASSERT_POINTER(system); |
| MALI_DEBUG_ASSERT_POINTER(system->session); |
| MALI_DEBUG_ASSERT(system->session->is_aborting); |
| |
| MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting soft job system for session 0x%08X.\n", system->session)); |
| |
| mali_soft_job_system_lock(system); |
| |
| _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &system->jobs_used, struct mali_soft_job, system_list) { |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state || |
| MALI_SOFT_JOB_STATE_TIMED_OUT == job->state); |
| |
| if (MALI_SOFT_JOB_STATE_STARTED == job->state) { |
| /* If the job has been activated, we have to release the tracker and destroy |
| * the job. If not, the tracker will be released and the job destroyed when |
| * it is activated. */ |
| if (MALI_TRUE == job->activated) { |
| MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting unsignaled soft job %u (0x%08X).\n", job->id, job)); |
| |
| job->state = MALI_SOFT_JOB_STATE_SIGNALED; |
| _mali_osk_list_move(&job->system_list, &jobs); |
| } |
| } else if (MALI_SOFT_JOB_STATE_TIMED_OUT == job->state) { |
| MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting timed out soft job %u (0x%08X).\n", job->id, job)); |
| |
| /* We need to destroy this soft job. */ |
| _mali_osk_list_move(&job->system_list, &jobs); |
| } |
| } |
| |
| mali_soft_job_system_unlock(system); |
| |
| /* Release and destroy jobs. */ |
| _MALI_OSK_LIST_FOREACHENTRY(job, tmp, &jobs, struct mali_soft_job, system_list) { |
| MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_SIGNALED == job->state || |
| MALI_SOFT_JOB_STATE_TIMED_OUT == job->state); |
| |
| if (MALI_SOFT_JOB_STATE_SIGNALED == job->state) { |
| mali_timeline_tracker_release(&job->tracker); |
| } |
| |
| /* Move job back to used list before destroying. */ |
| _mali_osk_list_move(&job->system_list, &system->jobs_used); |
| |
| mali_soft_job_destroy(job); |
| } |
| |
| mali_executor_schedule_from_mask(MALI_SCHEDULER_MASK_ALL, MALI_FALSE); |
| } |