| // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note |
| /* |
| * |
| * (C) COPYRIGHT 2014-2022 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 license. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can access it online at |
| * http://www.gnu.org/licenses/gpl-2.0.html. |
| * |
| */ |
| |
| /* |
| * Register-based HW access backend specific APIs |
| */ |
| |
| #include <mali_kbase.h> |
| #include <gpu/mali_kbase_gpu_fault.h> |
| #include <mali_kbase_hwaccess_jm.h> |
| #include <mali_kbase_jm.h> |
| #include <mali_kbase_js.h> |
| #include <tl/mali_kbase_tracepoints.h> |
| #include <mali_kbase_hwcnt_context.h> |
| #include <mali_kbase_reset_gpu.h> |
| #include <mali_kbase_kinstr_jm.h> |
| #include <backend/gpu/mali_kbase_cache_policy_backend.h> |
| #include <device/mali_kbase_device.h> |
| #include <backend/gpu/mali_kbase_jm_internal.h> |
| #include <backend/gpu/mali_kbase_pm_internal.h> |
| |
| /** |
| * SLOT_RB_EMPTY - Return whether the specified ringbuffer is empty. |
| * |
| * @rb: ring buffer |
| * |
| * Note: HW access lock must be held |
| */ |
| #define SLOT_RB_EMPTY(rb) (rb->write_idx == rb->read_idx) |
| |
| /** |
| * SLOT_RB_ENTRIES - Return number of atoms currently in the specified ringbuffer. |
| * |
| * @rb: ring buffer |
| * |
| * Note: HW access lock must be held |
| */ |
| #define SLOT_RB_ENTRIES(rb) ((int)(s8)(rb->write_idx - rb->read_idx)) |
| |
| static void kbase_gpu_release_atom(struct kbase_device *kbdev, |
| struct kbase_jd_atom *katom, |
| ktime_t *end_timestamp); |
| |
| /** |
| * kbase_gpu_enqueue_atom - Enqueue an atom in the HW access ringbuffer |
| * @kbdev: Device pointer |
| * @katom: Atom to enqueue |
| * |
| * Context: Caller must hold the HW access lock |
| */ |
| static void kbase_gpu_enqueue_atom(struct kbase_device *kbdev, |
| struct kbase_jd_atom *katom) |
| { |
| struct slot_rb *rb = &kbdev->hwaccess.backend.slot_rb[katom->slot_nr]; |
| |
| WARN_ON(SLOT_RB_ENTRIES(rb) >= SLOT_RB_SIZE); |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| rb->entries[rb->write_idx & SLOT_RB_MASK].katom = katom; |
| rb->write_idx++; |
| |
| katom->gpu_rb_state = KBASE_ATOM_GPU_RB_WAITING_BLOCKED; |
| } |
| |
| /** |
| * kbase_gpu_dequeue_atom - Remove an atom from the HW access ringbuffer, once |
| * it has been completed |
| * @kbdev: Device pointer |
| * @js: Job slot to remove atom from |
| * @end_timestamp: Pointer to timestamp of atom completion. May be NULL, in |
| * which case current time will be used. |
| * |
| * Context: Caller must hold the HW access lock |
| * |
| * Return: Atom removed from ringbuffer |
| */ |
| static struct kbase_jd_atom *kbase_gpu_dequeue_atom(struct kbase_device *kbdev, |
| int js, |
| ktime_t *end_timestamp) |
| { |
| struct slot_rb *rb = &kbdev->hwaccess.backend.slot_rb[js]; |
| struct kbase_jd_atom *katom; |
| |
| if (SLOT_RB_EMPTY(rb)) { |
| WARN(1, "GPU ringbuffer unexpectedly empty\n"); |
| return NULL; |
| } |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| katom = rb->entries[rb->read_idx & SLOT_RB_MASK].katom; |
| |
| kbase_gpu_release_atom(kbdev, katom, end_timestamp); |
| |
| rb->read_idx++; |
| |
| katom->gpu_rb_state = KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB; |
| |
| return katom; |
| } |
| |
| struct kbase_jd_atom *kbase_gpu_inspect(struct kbase_device *kbdev, int js, |
| int idx) |
| { |
| struct slot_rb *rb = &kbdev->hwaccess.backend.slot_rb[js]; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| if ((SLOT_RB_ENTRIES(rb) - 1) < idx) |
| return NULL; /* idx out of range */ |
| |
| return rb->entries[(rb->read_idx + idx) & SLOT_RB_MASK].katom; |
| } |
| |
| struct kbase_jd_atom *kbase_backend_inspect_tail(struct kbase_device *kbdev, |
| int js) |
| { |
| struct slot_rb *rb = &kbdev->hwaccess.backend.slot_rb[js]; |
| |
| if (SLOT_RB_EMPTY(rb)) |
| return NULL; |
| |
| return rb->entries[(rb->write_idx - 1) & SLOT_RB_MASK].katom; |
| } |
| |
| bool kbase_gpu_atoms_submitted_any(struct kbase_device *kbdev) |
| { |
| int js; |
| int i; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| for (js = 0; js < kbdev->gpu_props.num_job_slots; js++) { |
| for (i = 0; i < SLOT_RB_SIZE; i++) { |
| struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, js, i); |
| |
| if (katom && katom->gpu_rb_state == KBASE_ATOM_GPU_RB_SUBMITTED) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| int kbase_backend_nr_atoms_submitted(struct kbase_device *kbdev, int js) |
| { |
| int nr = 0; |
| int i; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| for (i = 0; i < SLOT_RB_SIZE; i++) { |
| struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, js, i); |
| |
| if (katom && (katom->gpu_rb_state == |
| KBASE_ATOM_GPU_RB_SUBMITTED)) |
| nr++; |
| } |
| |
| return nr; |
| } |
| |
| int kbase_backend_nr_atoms_on_slot(struct kbase_device *kbdev, int js) |
| { |
| int nr = 0; |
| int i; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| for (i = 0; i < SLOT_RB_SIZE; i++) { |
| if (kbase_gpu_inspect(kbdev, js, i)) |
| nr++; |
| } |
| |
| return nr; |
| } |
| |
| static int kbase_gpu_nr_atoms_on_slot_min(struct kbase_device *kbdev, int js, |
| enum kbase_atom_gpu_rb_state min_rb_state) |
| { |
| int nr = 0; |
| int i; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| for (i = 0; i < SLOT_RB_SIZE; i++) { |
| struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, js, i); |
| |
| if (katom && (katom->gpu_rb_state >= min_rb_state)) |
| nr++; |
| } |
| |
| return nr; |
| } |
| |
| /** |
| * check_secure_atom - Check if the given atom is in the given secure state and |
| * has a ringbuffer state of at least |
| * KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_TRANSITION |
| * @katom: Atom pointer |
| * @secure: Desired secure state |
| * |
| * Return: true if atom is in the given state, false otherwise |
| */ |
| static bool check_secure_atom(struct kbase_jd_atom *katom, bool secure) |
| { |
| if (katom->gpu_rb_state >= |
| KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_TRANSITION && |
| ((kbase_jd_katom_is_protected(katom) && secure) || |
| (!kbase_jd_katom_is_protected(katom) && !secure))) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * kbase_gpu_check_secure_atoms - Check if there are any atoms in the given |
| * secure state in the ringbuffers of at least |
| * state |
| * KBASE_ATOM_GPU_RB_WAITING_FOR_CORE_AVAILABLE |
| * @kbdev: Device pointer |
| * @secure: Desired secure state |
| * |
| * Return: true if any atoms are in the given state, false otherwise |
| */ |
| static bool kbase_gpu_check_secure_atoms(struct kbase_device *kbdev, |
| bool secure) |
| { |
| int js, i; |
| |
| for (js = 0; js < kbdev->gpu_props.num_job_slots; js++) { |
| for (i = 0; i < SLOT_RB_SIZE; i++) { |
| struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, |
| js, i); |
| |
| if (katom) { |
| if (check_secure_atom(katom, secure)) |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| int kbase_backend_slot_free(struct kbase_device *kbdev, int js) |
| { |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| if (atomic_read(&kbdev->hwaccess.backend.reset_gpu) != |
| KBASE_RESET_GPU_NOT_PENDING) { |
| /* The GPU is being reset - so prevent submission */ |
| return 0; |
| } |
| |
| return SLOT_RB_SIZE - kbase_backend_nr_atoms_on_slot(kbdev, js); |
| } |
| |
| |
| static void kbase_gpu_release_atom(struct kbase_device *kbdev, |
| struct kbase_jd_atom *katom, |
| ktime_t *end_timestamp) |
| { |
| struct kbase_context *kctx = katom->kctx; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| switch (katom->gpu_rb_state) { |
| case KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB: |
| /* Should be impossible */ |
| WARN(1, "Attempting to release atom not in ringbuffer\n"); |
| break; |
| |
| case KBASE_ATOM_GPU_RB_SUBMITTED: |
| kbase_kinstr_jm_atom_hw_release(katom); |
| /* Inform power management at start/finish of atom so it can |
| * update its GPU utilisation metrics. Mark atom as not |
| * submitted beforehand. |
| */ |
| katom->gpu_rb_state = KBASE_ATOM_GPU_RB_READY; |
| kbase_pm_metrics_update(kbdev, end_timestamp); |
| |
| /* Inform platform at start/finish of atom */ |
| kbasep_platform_event_atom_complete(katom); |
| |
| if (katom->core_req & BASE_JD_REQ_PERMON) |
| kbase_pm_release_gpu_cycle_counter_nolock(kbdev); |
| |
| KBASE_TLSTREAM_TL_NRET_ATOM_LPU(kbdev, katom, |
| &kbdev->gpu_props.props.raw_props.js_features |
| [katom->slot_nr]); |
| KBASE_TLSTREAM_TL_NRET_ATOM_AS(kbdev, katom, &kbdev->as[kctx->as_nr]); |
| KBASE_TLSTREAM_TL_NRET_CTX_LPU(kbdev, kctx, |
| &kbdev->gpu_props.props.raw_props.js_features |
| [katom->slot_nr]); |
| |
| /* ***FALLTHROUGH: TRANSITION TO LOWER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_READY: |
| /* ***FALLTHROUGH: TRANSITION TO LOWER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_WAITING_FOR_CORE_AVAILABLE: |
| break; |
| |
| case KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_TRANSITION: |
| if (kbase_jd_katom_is_protected(katom) && |
| (katom->protected_state.enter != |
| KBASE_ATOM_ENTER_PROTECTED_CHECK) && |
| (katom->protected_state.enter != |
| KBASE_ATOM_ENTER_PROTECTED_HWCNT)) { |
| kbase_pm_protected_override_disable(kbdev); |
| kbase_pm_update_cores_state_nolock(kbdev); |
| } |
| if (kbase_jd_katom_is_protected(katom) && |
| (katom->protected_state.enter == |
| KBASE_ATOM_ENTER_PROTECTED_IDLE_L2)) |
| kbase_pm_protected_entry_override_disable(kbdev); |
| if (!kbase_jd_katom_is_protected(katom) && |
| (katom->protected_state.exit != |
| KBASE_ATOM_EXIT_PROTECTED_CHECK) && |
| (katom->protected_state.exit != |
| KBASE_ATOM_EXIT_PROTECTED_RESET_WAIT)) { |
| kbase_pm_protected_override_disable(kbdev); |
| kbase_pm_update_cores_state_nolock(kbdev); |
| } |
| |
| if (katom->protected_state.enter != |
| KBASE_ATOM_ENTER_PROTECTED_CHECK || |
| katom->protected_state.exit != |
| KBASE_ATOM_EXIT_PROTECTED_CHECK) |
| kbdev->protected_mode_transition = false; |
| |
| /* If the atom is at KBASE_ATOM_ENTER_PROTECTED_HWCNT state, it means |
| * one of two events prevented it from progressing to the next state and |
| * ultimately reach protected mode: |
| * - hwcnts were enabled, and the atom had to schedule a worker to |
| * disable them. |
| * - the hwcnts were already disabled, but some other error occurred. |
| * In the first case, if the worker has not yet completed |
| * (kbdev->protected_mode_hwcnt_disabled == false), we need to re-enable |
| * them and signal to the worker they have already been enabled |
| */ |
| if (kbase_jd_katom_is_protected(katom) && |
| (katom->protected_state.enter == KBASE_ATOM_ENTER_PROTECTED_HWCNT)) { |
| kbdev->protected_mode_hwcnt_desired = true; |
| if (kbdev->protected_mode_hwcnt_disabled) { |
| kbase_hwcnt_context_enable(kbdev->hwcnt_gpu_ctx); |
| kbdev->protected_mode_hwcnt_disabled = false; |
| } |
| } |
| |
| /* If the atom has suspended hwcnt but has not yet entered |
| * protected mode, then resume hwcnt now. If the GPU is now in |
| * protected mode then hwcnt will be resumed by GPU reset so |
| * don't resume it here. |
| */ |
| if (kbase_jd_katom_is_protected(katom) && |
| ((katom->protected_state.enter == KBASE_ATOM_ENTER_PROTECTED_IDLE_L2) || |
| (katom->protected_state.enter == KBASE_ATOM_ENTER_PROTECTED_SET_COHERENCY) || |
| (katom->protected_state.enter == KBASE_ATOM_ENTER_PROTECTED_FINISHED))) { |
| WARN_ON(!kbdev->protected_mode_hwcnt_disabled); |
| kbdev->protected_mode_hwcnt_desired = true; |
| if (kbdev->protected_mode_hwcnt_disabled) { |
| kbase_hwcnt_context_enable( |
| kbdev->hwcnt_gpu_ctx); |
| kbdev->protected_mode_hwcnt_disabled = false; |
| } |
| } |
| |
| if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_TGOX_R1_1234)) { |
| if (katom->atom_flags & |
| KBASE_KATOM_FLAG_HOLDING_L2_REF_PROT) { |
| kbase_pm_protected_l2_override(kbdev, false); |
| katom->atom_flags &= |
| ~KBASE_KATOM_FLAG_HOLDING_L2_REF_PROT; |
| } |
| } |
| |
| /* ***FALLTHROUGH: TRANSITION TO LOWER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_PREV: |
| /* ***FALLTHROUGH: TRANSITION TO LOWER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_WAITING_BLOCKED: |
| /* ***FALLTHROUGH: TRANSITION TO LOWER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_RETURN_TO_JS: |
| break; |
| } |
| |
| katom->gpu_rb_state = KBASE_ATOM_GPU_RB_WAITING_BLOCKED; |
| katom->protected_state.exit = KBASE_ATOM_EXIT_PROTECTED_CHECK; |
| } |
| |
| static void kbase_gpu_mark_atom_for_return(struct kbase_device *kbdev, |
| struct kbase_jd_atom *katom) |
| { |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| KBASE_KTRACE_ADD_JM_SLOT_INFO(kbdev, JM_MARK_FOR_RETURN_TO_JS, |
| katom->kctx, katom, katom->jc, |
| katom->slot_nr, katom->event_code); |
| kbase_gpu_release_atom(kbdev, katom, NULL); |
| katom->gpu_rb_state = KBASE_ATOM_GPU_RB_RETURN_TO_JS; |
| } |
| |
| /** |
| * other_slots_busy - Determine if any job slots other than @js are currently |
| * running atoms |
| * @kbdev: Device pointer |
| * @js: Job slot |
| * |
| * Return: true if any slots other than @js are busy, false otherwise |
| */ |
| static inline bool other_slots_busy(struct kbase_device *kbdev, int js) |
| { |
| int slot; |
| |
| for (slot = 0; slot < kbdev->gpu_props.num_job_slots; slot++) { |
| if (slot == js) |
| continue; |
| |
| if (kbase_gpu_nr_atoms_on_slot_min(kbdev, slot, |
| KBASE_ATOM_GPU_RB_SUBMITTED)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static inline bool kbase_gpu_in_protected_mode(struct kbase_device *kbdev) |
| { |
| return kbdev->protected_mode; |
| } |
| |
| static void kbase_gpu_disable_coherent(struct kbase_device *kbdev) |
| { |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| /* |
| * When entering into protected mode, we must ensure that the |
| * GPU is not operating in coherent mode as well. This is to |
| * ensure that no protected memory can be leaked. |
| */ |
| if (kbdev->system_coherency == COHERENCY_ACE) |
| kbase_cache_set_coherency_mode(kbdev, COHERENCY_ACE_LITE); |
| } |
| |
| static int kbase_gpu_protected_mode_enter(struct kbase_device *kbdev) |
| { |
| int err = -EINVAL; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| WARN_ONCE(!kbdev->protected_ops, |
| "Cannot enter protected mode: protected callbacks not specified.\n"); |
| |
| if (kbdev->protected_ops) { |
| /* Switch GPU to protected mode */ |
| err = kbdev->protected_ops->protected_mode_enable( |
| kbdev->protected_dev); |
| |
| if (err) { |
| dev_warn(kbdev->dev, "Failed to enable protected mode: %d\n", |
| err); |
| } else { |
| kbdev->protected_mode = true; |
| kbase_ipa_protection_mode_switch_event(kbdev); |
| } |
| } |
| |
| return err; |
| } |
| |
| static int kbase_gpu_protected_mode_reset(struct kbase_device *kbdev) |
| { |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| WARN_ONCE(!kbdev->protected_ops, |
| "Cannot exit protected mode: protected callbacks not specified.\n"); |
| |
| if (!kbdev->protected_ops) |
| return -EINVAL; |
| |
| /* The protected mode disable callback will be called as part of reset |
| */ |
| return kbase_reset_gpu_silent(kbdev); |
| } |
| |
| static int kbase_jm_protected_entry(struct kbase_device *kbdev, |
| struct kbase_jd_atom **katom, int idx, int js) |
| { |
| int err = 0; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| err = kbase_gpu_protected_mode_enter(kbdev); |
| |
| /* |
| * Regardless of result before this call, we are no longer |
| * transitioning the GPU. |
| */ |
| |
| kbdev->protected_mode_transition = false; |
| kbase_pm_protected_override_disable(kbdev); |
| kbase_pm_update_cores_state_nolock(kbdev); |
| |
| KBASE_TLSTREAM_AUX_PROTECTED_ENTER_END(kbdev, kbdev); |
| if (err) { |
| /* |
| * Failed to switch into protected mode. |
| * |
| * At this point we expect: |
| * katom->gpu_rb_state = KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_TRANSITION && |
| * katom->protected_state.enter = KBASE_ATOM_ENTER_PROTECTED_FINISHED |
| * ==> |
| * kbdev->protected_mode_hwcnt_disabled = false |
| */ |
| katom[idx]->event_code = BASE_JD_EVENT_JOB_INVALID; |
| kbase_gpu_mark_atom_for_return(kbdev, katom[idx]); |
| /* |
| * Only return if head atom or previous atom |
| * already removed - as atoms must be returned |
| * in order. |
| */ |
| if (idx == 0 || katom[0]->gpu_rb_state == |
| KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB) { |
| kbase_gpu_dequeue_atom(kbdev, js, NULL); |
| kbase_jm_return_atom_to_js(kbdev, katom[idx]); |
| } |
| |
| return -EINVAL; |
| } |
| |
| /* |
| * Protected mode sanity checks. |
| */ |
| WARN(kbase_jd_katom_is_protected(katom[idx]) != kbase_gpu_in_protected_mode(kbdev), |
| "Protected mode of atom (%d) doesn't match protected mode of GPU (%d)", |
| kbase_jd_katom_is_protected(katom[idx]), kbase_gpu_in_protected_mode(kbdev)); |
| katom[idx]->gpu_rb_state = |
| KBASE_ATOM_GPU_RB_READY; |
| |
| return err; |
| } |
| |
| static int kbase_jm_enter_protected_mode(struct kbase_device *kbdev, |
| struct kbase_jd_atom **katom, int idx, int js) |
| { |
| int err = 0; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| switch (katom[idx]->protected_state.enter) { |
| case KBASE_ATOM_ENTER_PROTECTED_CHECK: |
| KBASE_TLSTREAM_AUX_PROTECTED_ENTER_START(kbdev, kbdev); |
| /* The checks in KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_PREV |
| * should ensure that we are not already transition, and that |
| * there are no atoms currently on the GPU. |
| */ |
| WARN_ON(kbdev->protected_mode_transition); |
| WARN_ON(kbase_gpu_atoms_submitted_any(kbdev)); |
| /* If hwcnt is disabled, it means we didn't clean up correctly |
| * during last exit from protected mode. |
| */ |
| WARN_ON(kbdev->protected_mode_hwcnt_disabled); |
| |
| katom[idx]->protected_state.enter = |
| KBASE_ATOM_ENTER_PROTECTED_HWCNT; |
| |
| kbdev->protected_mode_transition = true; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_ENTER_PROTECTED_HWCNT: |
| /* See if we can get away with disabling hwcnt atomically */ |
| kbdev->protected_mode_hwcnt_desired = false; |
| if (!kbdev->protected_mode_hwcnt_disabled) { |
| if (kbase_hwcnt_context_disable_atomic( |
| kbdev->hwcnt_gpu_ctx)) |
| kbdev->protected_mode_hwcnt_disabled = true; |
| } |
| |
| /* We couldn't disable atomically, so kick off a worker */ |
| if (!kbdev->protected_mode_hwcnt_disabled) { |
| kbase_hwcnt_context_queue_work( |
| kbdev->hwcnt_gpu_ctx, |
| &kbdev->protected_mode_hwcnt_disable_work); |
| return -EAGAIN; |
| } |
| |
| /* Once reaching this point GPU must be switched to protected |
| * mode or hwcnt re-enabled. |
| */ |
| |
| if (kbase_pm_protected_entry_override_enable(kbdev)) |
| return -EAGAIN; |
| |
| /* |
| * Not in correct mode, begin protected mode switch. |
| * Entering protected mode requires us to power down the L2, |
| * and drop out of fully coherent mode. |
| */ |
| katom[idx]->protected_state.enter = |
| KBASE_ATOM_ENTER_PROTECTED_IDLE_L2; |
| |
| kbase_pm_protected_override_enable(kbdev); |
| /* |
| * Only if the GPU reset hasn't been initiated, there is a need |
| * to invoke the state machine to explicitly power down the |
| * shader cores and L2. |
| */ |
| if (!kbdev->pm.backend.protected_entry_transition_override) |
| kbase_pm_update_cores_state_nolock(kbdev); |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_ENTER_PROTECTED_IDLE_L2: |
| /* Avoid unnecessary waiting on non-ACE platforms. */ |
| if (kbdev->system_coherency == COHERENCY_ACE) { |
| if (kbdev->pm.backend.l2_always_on) { |
| /* |
| * If the GPU reset hasn't completed, then L2 |
| * could still be powered up. |
| */ |
| if (kbase_reset_gpu_is_active(kbdev)) |
| return -EAGAIN; |
| } |
| |
| if (kbase_pm_get_ready_cores(kbdev, |
| KBASE_PM_CORE_L2) || |
| kbase_pm_get_trans_cores(kbdev, |
| KBASE_PM_CORE_L2) || |
| kbase_is_gpu_removed(kbdev)) { |
| /* |
| * The L2 is still powered, wait for all |
| * the users to finish with it before doing |
| * the actual reset. |
| */ |
| return -EAGAIN; |
| } |
| } |
| |
| katom[idx]->protected_state.enter = |
| KBASE_ATOM_ENTER_PROTECTED_SET_COHERENCY; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_ENTER_PROTECTED_SET_COHERENCY: |
| /* |
| * When entering into protected mode, we must ensure that the |
| * GPU is not operating in coherent mode as well. This is to |
| * ensure that no protected memory can be leaked. |
| */ |
| kbase_gpu_disable_coherent(kbdev); |
| |
| kbase_pm_protected_entry_override_disable(kbdev); |
| |
| if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_TGOX_R1_1234)) { |
| /* |
| * Power on L2 caches; this will also result in the |
| * correct value written to coherency enable register. |
| */ |
| kbase_pm_protected_l2_override(kbdev, true); |
| |
| /* |
| * Set the flag on the atom that additional |
| * L2 references are taken. |
| */ |
| katom[idx]->atom_flags |= |
| KBASE_KATOM_FLAG_HOLDING_L2_REF_PROT; |
| } |
| |
| katom[idx]->protected_state.enter = |
| KBASE_ATOM_ENTER_PROTECTED_FINISHED; |
| |
| if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_TGOX_R1_1234)) |
| return -EAGAIN; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_ENTER_PROTECTED_FINISHED: |
| if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_TGOX_R1_1234)) { |
| /* |
| * Check that L2 caches are powered and, if so, |
| * enter protected mode. |
| */ |
| if (kbdev->pm.backend.l2_state == KBASE_L2_ON) { |
| /* |
| * Remove additional L2 reference and reset |
| * the atom flag which denotes it. |
| */ |
| if (katom[idx]->atom_flags & |
| KBASE_KATOM_FLAG_HOLDING_L2_REF_PROT) { |
| kbase_pm_protected_l2_override(kbdev, |
| false); |
| katom[idx]->atom_flags &= |
| ~KBASE_KATOM_FLAG_HOLDING_L2_REF_PROT; |
| } |
| |
| err = kbase_jm_protected_entry(kbdev, katom, idx, js); |
| |
| if (err) |
| return err; |
| } else { |
| /* |
| * still waiting for L2 caches to power up |
| */ |
| return -EAGAIN; |
| } |
| } else { |
| err = kbase_jm_protected_entry(kbdev, katom, idx, js); |
| |
| if (err) |
| return err; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int kbase_jm_exit_protected_mode(struct kbase_device *kbdev, |
| struct kbase_jd_atom **katom, int idx, int js) |
| { |
| int err = 0; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| switch (katom[idx]->protected_state.exit) { |
| case KBASE_ATOM_EXIT_PROTECTED_CHECK: |
| KBASE_TLSTREAM_AUX_PROTECTED_LEAVE_START(kbdev, kbdev); |
| /* The checks in KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_PREV |
| * should ensure that we are not already transition, and that |
| * there are no atoms currently on the GPU. |
| */ |
| WARN_ON(kbdev->protected_mode_transition); |
| WARN_ON(kbase_gpu_atoms_submitted_any(kbdev)); |
| |
| /* |
| * Exiting protected mode requires a reset, but first the L2 |
| * needs to be powered down to ensure it's not active when the |
| * reset is issued. |
| */ |
| katom[idx]->protected_state.exit = |
| KBASE_ATOM_EXIT_PROTECTED_IDLE_L2; |
| |
| kbdev->protected_mode_transition = true; |
| kbase_pm_protected_override_enable(kbdev); |
| kbase_pm_update_cores_state_nolock(kbdev); |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_EXIT_PROTECTED_IDLE_L2: |
| if (kbdev->pm.backend.l2_state != KBASE_L2_OFF) { |
| /* |
| * The L2 is still powered, wait for all the users to |
| * finish with it before doing the actual reset. |
| */ |
| return -EAGAIN; |
| } |
| katom[idx]->protected_state.exit = |
| KBASE_ATOM_EXIT_PROTECTED_RESET; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_EXIT_PROTECTED_RESET: |
| /* L2 cache has been turned off (which is needed prior to the reset of GPU |
| * to exit the protected mode), so the override flag can be safely cleared. |
| * Even if L2 cache is powered up again before the actual reset, it should |
| * not be an issue (there are no jobs running on the GPU). |
| */ |
| kbase_pm_protected_override_disable(kbdev); |
| |
| /* Issue the reset to the GPU */ |
| err = kbase_gpu_protected_mode_reset(kbdev); |
| |
| if (err == -EAGAIN) |
| return -EAGAIN; |
| |
| if (err) { |
| kbdev->protected_mode_transition = false; |
| |
| /* Failed to exit protected mode, fail atom */ |
| katom[idx]->event_code = BASE_JD_EVENT_JOB_INVALID; |
| kbase_gpu_mark_atom_for_return(kbdev, katom[idx]); |
| /* Only return if head atom or previous atom |
| * already removed - as atoms must be returned in order |
| */ |
| if (idx == 0 || katom[0]->gpu_rb_state == |
| KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB) { |
| kbase_gpu_dequeue_atom(kbdev, js, NULL); |
| kbase_jm_return_atom_to_js(kbdev, katom[idx]); |
| } |
| |
| /* If we're exiting from protected mode, hwcnt must have |
| * been disabled during entry. |
| */ |
| WARN_ON(!kbdev->protected_mode_hwcnt_disabled); |
| kbdev->protected_mode_hwcnt_desired = true; |
| if (kbdev->protected_mode_hwcnt_disabled) { |
| kbase_hwcnt_context_enable( |
| kbdev->hwcnt_gpu_ctx); |
| kbdev->protected_mode_hwcnt_disabled = false; |
| } |
| |
| return -EINVAL; |
| } |
| |
| katom[idx]->protected_state.exit = |
| KBASE_ATOM_EXIT_PROTECTED_RESET_WAIT; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_EXIT_PROTECTED_RESET_WAIT: |
| /* A GPU reset is issued when exiting protected mode. Once the |
| * reset is done all atoms' state will also be reset. For this |
| * reason, if the atom is still in this state we can safely |
| * say that the reset has not completed i.e., we have not |
| * finished exiting protected mode yet. |
| */ |
| return -EAGAIN; |
| } |
| |
| return 0; |
| } |
| |
| void kbase_backend_slot_update(struct kbase_device *kbdev) |
| { |
| int js; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| #ifdef CONFIG_MALI_ARBITER_SUPPORT |
| if (kbase_reset_gpu_is_active(kbdev) || |
| kbase_is_gpu_removed(kbdev)) |
| #else |
| if (kbase_reset_gpu_is_active(kbdev)) |
| #endif |
| return; |
| |
| for (js = 0; js < kbdev->gpu_props.num_job_slots; js++) { |
| struct kbase_jd_atom *katom[2]; |
| int idx; |
| |
| katom[0] = kbase_gpu_inspect(kbdev, js, 0); |
| katom[1] = kbase_gpu_inspect(kbdev, js, 1); |
| WARN_ON(katom[1] && !katom[0]); |
| |
| for (idx = 0; idx < SLOT_RB_SIZE; idx++) { |
| bool cores_ready; |
| int ret; |
| |
| if (!katom[idx]) |
| continue; |
| |
| switch (katom[idx]->gpu_rb_state) { |
| case KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB: |
| /* Should be impossible */ |
| WARN(1, "Attempting to update atom not in ringbuffer\n"); |
| break; |
| |
| case KBASE_ATOM_GPU_RB_WAITING_BLOCKED: |
| if (kbase_js_atom_blocked_on_x_dep(katom[idx])) |
| break; |
| |
| katom[idx]->gpu_rb_state = |
| KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_PREV; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_PREV: |
| if (kbase_gpu_check_secure_atoms(kbdev, |
| !kbase_jd_katom_is_protected( |
| katom[idx]))) |
| break; |
| |
| if ((idx == 1) && (kbase_jd_katom_is_protected( |
| katom[0]) != |
| kbase_jd_katom_is_protected( |
| katom[1]))) |
| break; |
| |
| if (kbdev->protected_mode_transition) |
| break; |
| |
| katom[idx]->gpu_rb_state = |
| KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_TRANSITION; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_WAITING_PROTECTED_MODE_TRANSITION: |
| |
| /* |
| * Exiting protected mode must be done before |
| * the references on the cores are taken as |
| * a power down the L2 is required which |
| * can't happen after the references for this |
| * atom are taken. |
| */ |
| |
| if (!kbase_gpu_in_protected_mode(kbdev) && |
| kbase_jd_katom_is_protected(katom[idx])) { |
| /* Atom needs to transition into protected mode. */ |
| ret = kbase_jm_enter_protected_mode(kbdev, |
| katom, idx, js); |
| if (ret) |
| break; |
| } else if (kbase_gpu_in_protected_mode(kbdev) && |
| !kbase_jd_katom_is_protected(katom[idx])) { |
| /* Atom needs to transition out of protected mode. */ |
| ret = kbase_jm_exit_protected_mode(kbdev, |
| katom, idx, js); |
| if (ret) |
| break; |
| } |
| katom[idx]->protected_state.exit = |
| KBASE_ATOM_EXIT_PROTECTED_CHECK; |
| |
| /* Atom needs no protected mode transition. */ |
| |
| katom[idx]->gpu_rb_state = |
| KBASE_ATOM_GPU_RB_WAITING_FOR_CORE_AVAILABLE; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_WAITING_FOR_CORE_AVAILABLE: |
| if (katom[idx]->will_fail_event_code) { |
| kbase_gpu_mark_atom_for_return(kbdev, |
| katom[idx]); |
| /* Set EVENT_DONE so this atom will be |
| * completed, not unpulled. |
| */ |
| katom[idx]->event_code = |
| BASE_JD_EVENT_DONE; |
| /* Only return if head atom or previous |
| * atom already removed - as atoms must |
| * be returned in order. |
| */ |
| if (idx == 0 || katom[0]->gpu_rb_state == |
| KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB) { |
| kbase_gpu_dequeue_atom(kbdev, js, NULL); |
| kbase_jm_return_atom_to_js(kbdev, katom[idx]); |
| } |
| break; |
| } |
| |
| cores_ready = kbase_pm_cores_requested(kbdev, |
| true); |
| |
| if (!cores_ready) |
| break; |
| |
| katom[idx]->gpu_rb_state = |
| KBASE_ATOM_GPU_RB_READY; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_READY: |
| |
| if (idx == 1) { |
| /* Only submit if head atom or previous |
| * atom already submitted |
| */ |
| if ((katom[0]->gpu_rb_state != |
| KBASE_ATOM_GPU_RB_SUBMITTED && |
| katom[0]->gpu_rb_state != |
| KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB)) |
| break; |
| |
| /* If intra-slot serialization in use |
| * then don't submit atom to NEXT slot |
| */ |
| if (kbdev->serialize_jobs & |
| KBASE_SERIALIZE_INTRA_SLOT) |
| break; |
| } |
| |
| /* If inter-slot serialization in use then don't |
| * submit atom if any other slots are in use |
| */ |
| if ((kbdev->serialize_jobs & |
| KBASE_SERIALIZE_INTER_SLOT) && |
| other_slots_busy(kbdev, js)) |
| break; |
| |
| #ifdef CONFIG_MALI_GEM5_BUILD |
| if (!kbasep_jm_is_js_free(kbdev, js, |
| katom[idx]->kctx)) |
| break; |
| #endif |
| /* Check if this job needs the cycle counter |
| * enabled before submission |
| */ |
| if (katom[idx]->core_req & BASE_JD_REQ_PERMON) |
| kbase_pm_request_gpu_cycle_counter_l2_is_on( |
| kbdev); |
| |
| if (!kbase_job_hw_submit(kbdev, katom[idx], js)) |
| katom[idx]->gpu_rb_state = KBASE_ATOM_GPU_RB_SUBMITTED; |
| else |
| break; |
| |
| /* ***TRANSITION TO HIGHER STATE*** */ |
| fallthrough; |
| case KBASE_ATOM_GPU_RB_SUBMITTED: |
| |
| /* Inform power management at start/finish of |
| * atom so it can update its GPU utilisation |
| * metrics. |
| */ |
| kbase_pm_metrics_update(kbdev, |
| &katom[idx]->start_timestamp); |
| |
| /* Inform platform at start/finish of atom */ |
| kbasep_platform_event_atom_submit(katom[idx]); |
| |
| break; |
| |
| case KBASE_ATOM_GPU_RB_RETURN_TO_JS: |
| /* Only return if head atom or previous atom |
| * already removed - as atoms must be returned |
| * in order |
| */ |
| if (idx == 0 || katom[0]->gpu_rb_state == |
| KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB) { |
| kbase_gpu_dequeue_atom(kbdev, js, NULL); |
| kbase_jm_return_atom_to_js(kbdev, |
| katom[idx]); |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| |
| void kbase_backend_run_atom(struct kbase_device *kbdev, |
| struct kbase_jd_atom *katom) |
| { |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| dev_dbg(kbdev->dev, "Backend running atom %pK\n", (void *)katom); |
| |
| kbase_gpu_enqueue_atom(kbdev, katom); |
| kbase_backend_slot_update(kbdev); |
| } |
| |
| /** |
| * kbase_rb_atom_might_depend - determine if one atom in the slot ringbuffer |
| * might depend on another from the same kctx |
| * @katom_a: depended atom |
| * @katom_b: atom to query |
| * |
| * This can be used on atoms that belong to different slot ringbuffers |
| * |
| * Return: true if @katom_b might depend on @katom_a, false if it cannot depend. |
| */ |
| static inline bool |
| kbase_rb_atom_might_depend(const struct kbase_jd_atom *katom_a, |
| const struct kbase_jd_atom *katom_b) |
| { |
| if (katom_a->kctx != katom_b->kctx) |
| return false; |
| return (katom_b->pre_dep || |
| (katom_b->atom_flags & (KBASE_KATOM_FLAG_X_DEP_BLOCKED | |
| KBASE_KATOM_FLAG_FAIL_BLOCKER))); |
| } |
| |
| /** |
| * kbase_gpu_irq_evict - evict a slot's JSn_HEAD_NEXT atom from the HW if it is |
| * related to a failed JSn_HEAD atom |
| * @kbdev: kbase device |
| * @js: job slot to check |
| * @completion_code: completion code of the failed atom |
| * |
| * Note: 'STOPPED' atoms are considered 'failed', as they are in the HW, but |
| * unlike other failure codes we _can_ re-run them. |
| * |
| * This forms step 1 in a 2-step process of removing any related atoms from a |
| * slot's JSn_HEAD_NEXT (ringbuffer index 1), should there have |
| * been a 'failure' on an atom in JSn_HEAD (ringbuffer index 0). |
| * |
| * This step only removes the atoms from the HW, and marks them as |
| * (potentially) ready to run again. |
| * |
| * Step 2 is on marking the JSn_HEAD atom as complete |
| * (kbase_gpu_complete_hw()), to dequeue said atoms and return them to the JS |
| * as appropriate, or re-submit them. |
| * |
| * Hence, this function must evict at a minimum the atoms related to the atom |
| * in JSn_HEAD that kbase_gpu_complete_hw() will also dequeue. It is acceptable |
| * if this function evicts more atoms than kbase_gpu_complete_hw() dequeues, as |
| * the next kbase_backend_slot_update() will resubmit any remaining. |
| * |
| * Return: true if an atom was evicted, false otherwise. |
| */ |
| bool kbase_gpu_irq_evict(struct kbase_device *kbdev, int js, |
| u32 completion_code) |
| { |
| struct kbase_jd_atom *katom; |
| struct kbase_jd_atom *next_katom; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| katom = kbase_gpu_inspect(kbdev, js, 0); |
| next_katom = kbase_gpu_inspect(kbdev, js, 1); |
| |
| if (next_katom && |
| next_katom->gpu_rb_state == KBASE_ATOM_GPU_RB_SUBMITTED && |
| (kbase_rb_atom_might_depend(katom, next_katom) || |
| kbase_js_atom_runs_before(kbdev, katom, next_katom, 0u)) && |
| (kbase_reg_read(kbdev, JOB_SLOT_REG(js, JS_HEAD_NEXT_LO)) != 0 || |
| kbase_reg_read(kbdev, JOB_SLOT_REG(js, JS_HEAD_NEXT_HI)) != 0)) { |
| kbase_reg_write(kbdev, JOB_SLOT_REG(js, JS_COMMAND_NEXT), |
| JS_COMMAND_NOP); |
| next_katom->gpu_rb_state = KBASE_ATOM_GPU_RB_READY; |
| |
| if (completion_code == BASE_JD_EVENT_STOPPED) { |
| KBASE_TLSTREAM_TL_NRET_ATOM_LPU(kbdev, next_katom, |
| &kbdev->gpu_props.props.raw_props.js_features |
| [next_katom->slot_nr]); |
| KBASE_TLSTREAM_TL_NRET_ATOM_AS(kbdev, next_katom, &kbdev->as |
| [next_katom->kctx->as_nr]); |
| KBASE_TLSTREAM_TL_NRET_CTX_LPU(kbdev, next_katom->kctx, |
| &kbdev->gpu_props.props.raw_props.js_features |
| [next_katom->slot_nr]); |
| } |
| |
| if (next_katom->core_req & BASE_JD_REQ_PERMON) |
| kbase_pm_release_gpu_cycle_counter_nolock(kbdev); |
| |
| /* On evicting the next_katom, the last submission kctx on the |
| * given job slot then reverts back to the one that owns katom. |
| * The aim is to enable the next submission that can determine |
| * if the read only shader core L1 cache should be invalidated. |
| */ |
| kbdev->hwaccess.backend.slot_rb[js].last_kctx_tagged = |
| SLOT_RB_TAG_KCTX(katom->kctx); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * kbase_gpu_complete_hw - complete the atom in a slot's JSn_HEAD |
| * @kbdev: kbase device |
| * @js: job slot to check |
| * @completion_code: completion code of the completed atom |
| * @job_tail: value read from JSn_TAIL, for STOPPED atoms |
| * @end_timestamp: pointer to approximate ktime value when the katom completed |
| * |
| * Among other operations, this also executes step 2 of a 2-step process of |
| * removing any related atoms from a slot's JSn_HEAD_NEXT (ringbuffer index 1), |
| * should there have been a 'failure' on an atom in JSn_HEAD (ringbuffer index |
| * 0). The first step is done in kbase_gpu_irq_evict(). |
| * |
| * Note: 'STOPPED' atoms are considered 'failed', as they are in the HW, but |
| * unlike other failure codes we _can_ re-run them. |
| * |
| * When the JSn_HEAD atom is considered to be 'failed', then this will dequeue |
| * and return to the JS some (usually all) of the atoms evicted from the HW |
| * during the kbase_gpu_irq_evict() for that JSn_HEAD atom. If it dequeues an |
| * atom, that atom must not have been running or must already be evicted, as |
| * otherwise we would be in the incorrect state of having an atom both running |
| * on the HW and returned to the JS. |
| */ |
| |
| void kbase_gpu_complete_hw(struct kbase_device *kbdev, int js, |
| u32 completion_code, |
| u64 job_tail, |
| ktime_t *end_timestamp) |
| { |
| struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, js, 0); |
| struct kbase_context *kctx = katom->kctx; |
| |
| dev_dbg(kbdev->dev, |
| "Atom %pK completed on hw with code 0x%x and job_tail 0x%llx (s:%d)\n", |
| (void *)katom, completion_code, job_tail, js); |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| /* |
| * When a hard-stop is followed close after a soft-stop, the completion |
| * code may be set to STOPPED, even though the job is terminated |
| */ |
| if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_TMIX_8438)) { |
| if (completion_code == BASE_JD_EVENT_STOPPED && |
| (katom->atom_flags & |
| KBASE_KATOM_FLAG_BEEN_HARD_STOPPED)) { |
| completion_code = BASE_JD_EVENT_TERMINATED; |
| } |
| } |
| |
| if ((katom->core_req & BASE_JD_REQ_SKIP_CACHE_END) && |
| completion_code != BASE_JD_EVENT_DONE && |
| !(completion_code & BASE_JD_SW_EVENT)) { |
| /* When a job chain fails, on a T60x or when |
| * BASE_JD_REQ_SKIP_CACHE_END is set, the GPU cache is not |
| * flushed. To prevent future evictions causing possible memory |
| * corruption we need to flush the cache manually before any |
| * affected memory gets reused. |
| */ |
| katom->need_cache_flush_cores_retained = true; |
| } |
| |
| katom = kbase_gpu_dequeue_atom(kbdev, js, end_timestamp); |
| |
| if (completion_code == BASE_JD_EVENT_STOPPED) { |
| struct kbase_jd_atom *next_katom = kbase_gpu_inspect(kbdev, js, |
| 0); |
| |
| /* |
| * Dequeue next atom from ringbuffers on same slot if required. |
| * This atom will already have been removed from the NEXT |
| * registers by kbase_gpu_soft_hard_stop_slot(), to ensure that |
| * the atoms on this slot are returned in the correct order. |
| */ |
| if (next_katom && |
| kbase_js_atom_runs_before(kbdev, katom, next_katom, 0u)) { |
| WARN_ON(next_katom->gpu_rb_state == |
| KBASE_ATOM_GPU_RB_SUBMITTED); |
| kbase_gpu_dequeue_atom(kbdev, js, end_timestamp); |
| kbase_jm_return_atom_to_js(kbdev, next_katom); |
| } |
| } else if (completion_code != BASE_JD_EVENT_DONE) { |
| struct kbasep_js_device_data *js_devdata = &kbdev->js_data; |
| int i; |
| |
| if (!kbase_ctx_flag(katom->kctx, KCTX_DYING)) { |
| meson_gpu_data_invalid_count++; |
| dev_warn(kbdev->dev, "error detected from slot %d, job status 0x%08x (%s)", |
| js, completion_code, |
| kbase_gpu_exception_name( |
| completion_code)); |
| |
| } |
| |
| #if KBASE_KTRACE_DUMP_ON_JOB_SLOT_ERROR != 0 |
| KBASE_KTRACE_DUMP(kbdev); |
| #endif |
| kbasep_js_clear_submit_allowed(js_devdata, katom->kctx); |
| |
| /* |
| * Remove all atoms on the same context from ringbuffers. This |
| * will not remove atoms that are already on the GPU, as these |
| * are guaranteed not to have fail dependencies on the failed |
| * atom. |
| */ |
| for (i = 0; i < kbdev->gpu_props.num_job_slots; i++) { |
| struct kbase_jd_atom *katom_idx0 = |
| kbase_gpu_inspect(kbdev, i, 0); |
| struct kbase_jd_atom *katom_idx1 = |
| kbase_gpu_inspect(kbdev, i, 1); |
| |
| if (katom_idx0 && |
| kbase_rb_atom_might_depend(katom, katom_idx0) && |
| katom_idx0->gpu_rb_state != |
| KBASE_ATOM_GPU_RB_SUBMITTED) { |
| /* Dequeue katom_idx0 from ringbuffer */ |
| kbase_gpu_dequeue_atom(kbdev, i, end_timestamp); |
| |
| if (katom_idx1 && kbase_rb_atom_might_depend( |
| katom, katom_idx1) && |
| katom_idx0->gpu_rb_state != |
| KBASE_ATOM_GPU_RB_SUBMITTED) { |
| /* Dequeue katom_idx1 from ringbuffer */ |
| kbase_gpu_dequeue_atom(kbdev, i, |
| end_timestamp); |
| |
| katom_idx1->event_code = |
| BASE_JD_EVENT_STOPPED; |
| kbase_jm_return_atom_to_js(kbdev, |
| katom_idx1); |
| } |
| katom_idx0->event_code = BASE_JD_EVENT_STOPPED; |
| kbase_jm_return_atom_to_js(kbdev, katom_idx0); |
| |
| } else if (katom_idx1 && kbase_rb_atom_might_depend( |
| katom, katom_idx1) && |
| katom_idx1->gpu_rb_state != |
| KBASE_ATOM_GPU_RB_SUBMITTED) { |
| /* Can not dequeue this atom yet - will be |
| * dequeued when atom at idx0 completes |
| */ |
| katom_idx1->event_code = BASE_JD_EVENT_STOPPED; |
| kbase_gpu_mark_atom_for_return(kbdev, |
| katom_idx1); |
| } |
| } |
| } |
| |
| KBASE_KTRACE_ADD_JM_SLOT_INFO(kbdev, JM_JOB_DONE, kctx, katom, katom->jc, js, completion_code); |
| |
| if (job_tail != 0 && job_tail != katom->jc) { |
| /* Some of the job has been executed */ |
| dev_dbg(kbdev->dev, |
| "Update job chain address of atom %pK to resume from 0x%llx\n", |
| (void *)katom, job_tail); |
| |
| katom->jc = job_tail; |
| KBASE_KTRACE_ADD_JM_SLOT(kbdev, JM_UPDATE_HEAD, katom->kctx, |
| katom, job_tail, js); |
| } |
| |
| /* Only update the event code for jobs that weren't cancelled */ |
| if (katom->event_code != BASE_JD_EVENT_JOB_CANCELLED) |
| katom->event_code = (enum base_jd_event_code)completion_code; |
| |
| /* Complete the job, and start new ones |
| * |
| * Also defer remaining work onto the workqueue: |
| * - Re-queue Soft-stopped jobs |
| * - For any other jobs, queue the job back into the dependency system |
| * - Schedule out the parent context if necessary, and schedule a new |
| * one in. |
| */ |
| #if IS_ENABLED(CONFIG_GPU_TRACEPOINTS) |
| { |
| /* The atom in the HEAD */ |
| struct kbase_jd_atom *next_katom = kbase_gpu_inspect(kbdev, js, |
| 0); |
| |
| if (next_katom && next_katom->gpu_rb_state == |
| KBASE_ATOM_GPU_RB_SUBMITTED) { |
| char js_string[16]; |
| |
| trace_gpu_sched_switch(kbasep_make_job_slot_string(js, |
| js_string, |
| sizeof(js_string)), |
| ktime_to_ns(*end_timestamp), |
| (u32)next_katom->kctx->id, 0, |
| next_katom->work_id); |
| } else { |
| char js_string[16]; |
| |
| trace_gpu_sched_switch(kbasep_make_job_slot_string(js, js_string, |
| sizeof(js_string)), |
| ktime_to_ns(ktime_get_raw()), 0, 0, 0); |
| } |
| } |
| #endif |
| |
| if (kbdev->serialize_jobs & KBASE_SERIALIZE_RESET) |
| kbase_reset_gpu_silent(kbdev); |
| |
| if (completion_code == BASE_JD_EVENT_STOPPED) |
| katom = kbase_jm_return_atom_to_js(kbdev, katom); |
| else |
| katom = kbase_jm_complete(kbdev, katom, end_timestamp); |
| |
| if (katom) { |
| dev_dbg(kbdev->dev, |
| "Cross-slot dependency %pK has become runnable.\n", |
| (void *)katom); |
| |
| /* Check if there are lower priority jobs to soft stop */ |
| kbase_job_slot_ctx_priority_check_locked(kctx, katom); |
| |
| kbase_jm_try_kick(kbdev, 1 << katom->slot_nr); |
| } |
| |
| /* For partial shader core off L2 cache flush */ |
| kbase_pm_update_state(kbdev); |
| |
| /* Job completion may have unblocked other atoms. Try to update all job |
| * slots |
| */ |
| kbase_backend_slot_update(kbdev); |
| } |
| |
| void kbase_backend_reset(struct kbase_device *kbdev, ktime_t *end_timestamp) |
| { |
| int js; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| /* Reset should always take the GPU out of protected mode */ |
| WARN_ON(kbase_gpu_in_protected_mode(kbdev)); |
| |
| for (js = 0; js < kbdev->gpu_props.num_job_slots; js++) { |
| int atom_idx = 0; |
| int idx; |
| |
| for (idx = 0; idx < SLOT_RB_SIZE; idx++) { |
| struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, |
| js, atom_idx); |
| bool keep_in_jm_rb = false; |
| |
| if (!katom) |
| break; |
| if (katom->protected_state.exit == |
| KBASE_ATOM_EXIT_PROTECTED_RESET_WAIT) { |
| /* protected mode sanity checks */ |
| WARN(kbase_jd_katom_is_protected(katom) != |
| kbase_gpu_in_protected_mode(kbdev), |
| "Protected mode of atom (%d) doesn't match protected mode of GPU (%d)", |
| kbase_jd_katom_is_protected(katom), |
| kbase_gpu_in_protected_mode(kbdev)); |
| WARN(!(kbase_jd_katom_is_protected(katom) && js == 0) && |
| kbase_jd_katom_is_protected(katom), |
| "Protected atom on JS%d not supported", js); |
| } |
| if ((katom->gpu_rb_state < KBASE_ATOM_GPU_RB_SUBMITTED) && |
| !kbase_ctx_flag(katom->kctx, KCTX_DYING)) |
| keep_in_jm_rb = true; |
| |
| kbase_gpu_release_atom(kbdev, katom, NULL); |
| |
| /* |
| * If the atom wasn't on HW when the reset was issued |
| * then leave it in the RB and next time we're kicked |
| * it will be processed again from the starting state. |
| */ |
| if (keep_in_jm_rb) { |
| katom->protected_state.exit = KBASE_ATOM_EXIT_PROTECTED_CHECK; |
| /* As the atom was not removed, increment the |
| * index so that we read the correct atom in the |
| * next iteration. |
| */ |
| atom_idx++; |
| continue; |
| } |
| |
| /* |
| * The atom was on the HW when the reset was issued |
| * all we can do is fail the atom. |
| */ |
| kbase_gpu_dequeue_atom(kbdev, js, NULL); |
| katom->event_code = BASE_JD_EVENT_JOB_CANCELLED; |
| kbase_jm_complete(kbdev, katom, end_timestamp); |
| } |
| |
| /* Clear the slot's last katom submission kctx on reset */ |
| kbdev->hwaccess.backend.slot_rb[js].last_kctx_tagged = SLOT_RB_NULL_TAG_VAL; |
| } |
| |
| /* Re-enable GPU hardware counters if we're resetting from protected |
| * mode. |
| */ |
| kbdev->protected_mode_hwcnt_desired = true; |
| if (kbdev->protected_mode_hwcnt_disabled) { |
| kbase_hwcnt_context_enable(kbdev->hwcnt_gpu_ctx); |
| kbdev->protected_mode_hwcnt_disabled = false; |
| |
| KBASE_TLSTREAM_AUX_PROTECTED_LEAVE_END(kbdev, kbdev); |
| } |
| |
| kbdev->protected_mode_transition = false; |
| kbase_pm_protected_override_disable(kbdev); |
| } |
| |
| /** |
| * should_stop_next_atom - given a soft/hard stop action, determine if the next |
| * atom on a slot should be stopped |
| * @kbdev: kbase devices |
| * @head_katom: atom currently in the JSn_HEAD |
| * @next_katom: atom currently in the JSn_HEAD_NEXT |
| * @action: JS_COMMAND_<...> action for soft/hard-stop |
| * |
| * This is used in cases where @head_katom is the target of the soft/hard-stop. |
| * It only makes sense to call this when @head_katom and @next_katom are from |
| * the same slot. |
| * |
| * Return: true if @next_katom should also be stopped with the given action, |
| * false otherwise |
| */ |
| static bool should_stop_next_atom(struct kbase_device *kbdev, |
| const struct kbase_jd_atom *head_katom, |
| const struct kbase_jd_atom *next_katom, |
| u32 action) |
| { |
| bool ret = false; |
| u32 hw_action = action & JS_COMMAND_MASK; |
| |
| switch (hw_action) { |
| case JS_COMMAND_SOFT_STOP: |
| ret = kbase_js_atom_runs_before(kbdev, head_katom, next_katom, |
| 0u); |
| break; |
| case JS_COMMAND_HARD_STOP: |
| /* Unlike soft-stop, a hard-stop targeting a particular atom |
| * should not cause atoms from unrelated contexts to be |
| * removed |
| */ |
| ret = (head_katom->kctx == next_katom->kctx); |
| break; |
| default: |
| /* Other stop actions are possible, but the driver should not |
| * be generating them at this point in the call chain |
| */ |
| WARN(1, "Unexpected stop action: 0x%.8x", hw_action); |
| break; |
| } |
| return ret; |
| } |
| |
| static inline void kbase_gpu_stop_atom(struct kbase_device *kbdev, |
| int js, |
| struct kbase_jd_atom *katom, |
| u32 action) |
| { |
| struct kbase_context *kctx = katom->kctx; |
| u32 hw_action = action & JS_COMMAND_MASK; |
| |
| kbase_job_check_enter_disjoint(kbdev, action, katom->core_req, katom); |
| kbasep_job_slot_soft_or_hard_stop_do_action(kbdev, js, hw_action, |
| katom->core_req, katom); |
| kbase_jsctx_slot_prio_blocked_set(kctx, js, katom->sched_priority); |
| } |
| |
| static inline void kbase_gpu_remove_atom(struct kbase_device *kbdev, |
| struct kbase_jd_atom *katom, |
| u32 action, |
| bool disjoint) |
| { |
| struct kbase_context *kctx = katom->kctx; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| katom->event_code = BASE_JD_EVENT_REMOVED_FROM_NEXT; |
| kbase_gpu_mark_atom_for_return(kbdev, katom); |
| kbase_jsctx_slot_prio_blocked_set(kctx, katom->slot_nr, |
| katom->sched_priority); |
| |
| if (disjoint) |
| kbase_job_check_enter_disjoint(kbdev, action, katom->core_req, |
| katom); |
| } |
| |
| static int should_stop_x_dep_slot(struct kbase_jd_atom *katom) |
| { |
| if (katom->x_post_dep) { |
| struct kbase_jd_atom *dep_atom = katom->x_post_dep; |
| |
| if (dep_atom->gpu_rb_state != |
| KBASE_ATOM_GPU_RB_NOT_IN_SLOT_RB && |
| dep_atom->gpu_rb_state != |
| KBASE_ATOM_GPU_RB_RETURN_TO_JS) |
| return dep_atom->slot_nr; |
| } |
| return -1; |
| } |
| |
| bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev, |
| struct kbase_context *kctx, |
| int js, |
| struct kbase_jd_atom *katom, |
| u32 action) |
| { |
| struct kbase_jd_atom *katom_idx0; |
| struct kbase_context *kctx_idx0 = NULL; |
| struct kbase_jd_atom *katom_idx1; |
| struct kbase_context *kctx_idx1 = NULL; |
| |
| bool katom_idx0_valid, katom_idx1_valid; |
| |
| bool ret = false; |
| |
| int stop_x_dep_idx0 = -1, stop_x_dep_idx1 = -1; |
| int prio_idx0 = 0, prio_idx1 = 0; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| katom_idx0 = kbase_gpu_inspect(kbdev, js, 0); |
| katom_idx1 = kbase_gpu_inspect(kbdev, js, 1); |
| |
| if (katom_idx0) { |
| kctx_idx0 = katom_idx0->kctx; |
| prio_idx0 = katom_idx0->sched_priority; |
| } |
| if (katom_idx1) { |
| kctx_idx1 = katom_idx1->kctx; |
| prio_idx1 = katom_idx1->sched_priority; |
| } |
| |
| if (katom) { |
| katom_idx0_valid = (katom_idx0 == katom); |
| if (katom_idx1) |
| katom_idx1_valid = (katom_idx1 == katom); |
| else |
| katom_idx1_valid = false; |
| } else { |
| katom_idx0_valid = (katom_idx0 && (!kctx || kctx_idx0 == kctx)); |
| katom_idx1_valid = (katom_idx1 && (!kctx || kctx_idx1 == kctx)); |
| } |
| /* If there's an atom in JSn_HEAD_NEXT that we haven't already decided |
| * to stop, but we're stopping the JSn_HEAD atom, see if they are |
| * related/ordered in some way that would require the same stop action |
| */ |
| if (!katom_idx1_valid && katom_idx0_valid && katom_idx1) |
| katom_idx1_valid = should_stop_next_atom(kbdev, katom_idx0, |
| katom_idx1, action); |
| |
| if (katom_idx0_valid) |
| stop_x_dep_idx0 = should_stop_x_dep_slot(katom_idx0); |
| if (katom_idx1_valid) |
| stop_x_dep_idx1 = should_stop_x_dep_slot(katom_idx1); |
| |
| if (katom_idx0_valid) { |
| if (katom_idx0->gpu_rb_state != KBASE_ATOM_GPU_RB_SUBMITTED) { |
| /* Simple case - just dequeue and return */ |
| kbase_gpu_dequeue_atom(kbdev, js, NULL); |
| if (katom_idx1_valid) { |
| kbase_gpu_dequeue_atom(kbdev, js, NULL); |
| katom_idx1->event_code = |
| BASE_JD_EVENT_REMOVED_FROM_NEXT; |
| kbase_jm_return_atom_to_js(kbdev, katom_idx1); |
| kbase_jsctx_slot_prio_blocked_set(kctx_idx1, js, |
| prio_idx1); |
| } |
| |
| katom_idx0->event_code = |
| BASE_JD_EVENT_REMOVED_FROM_NEXT; |
| kbase_jm_return_atom_to_js(kbdev, katom_idx0); |
| kbase_jsctx_slot_prio_blocked_set(kctx_idx0, js, |
| prio_idx0); |
| } else { |
| /* katom_idx0 is on GPU */ |
| if (katom_idx1_valid && katom_idx1->gpu_rb_state == |
| KBASE_ATOM_GPU_RB_SUBMITTED) { |
| /* katom_idx0 and katom_idx1 are on GPU */ |
| |
| if (kbase_reg_read(kbdev, JOB_SLOT_REG(js, |
| JS_COMMAND_NEXT)) == 0) { |
| /* idx0 has already completed - stop |
| * idx1 if needed |
| */ |
| if (katom_idx1_valid) { |
| kbase_gpu_stop_atom(kbdev, js, |
| katom_idx1, |
| action); |
| ret = true; |
| } |
| } else { |
| /* idx1 is in NEXT registers - attempt |
| * to remove |
| */ |
| kbase_reg_write(kbdev, |
| JOB_SLOT_REG(js, |
| JS_COMMAND_NEXT), |
| JS_COMMAND_NOP); |
| |
| if (kbase_reg_read(kbdev, |
| JOB_SLOT_REG(js, |
| JS_HEAD_NEXT_LO)) |
| != 0 || |
| kbase_reg_read(kbdev, |
| JOB_SLOT_REG(js, |
| JS_HEAD_NEXT_HI)) |
| != 0) { |
| /* idx1 removed successfully, |
| * will be handled in IRQ |
| */ |
| kbase_gpu_remove_atom(kbdev, |
| katom_idx1, |
| action, true); |
| /* Revert the last_context. */ |
| kbdev->hwaccess.backend.slot_rb[js] |
| .last_kctx_tagged = |
| SLOT_RB_TAG_KCTX(katom_idx0->kctx); |
| |
| stop_x_dep_idx1 = |
| should_stop_x_dep_slot(katom_idx1); |
| |
| /* stop idx0 if still on GPU */ |
| kbase_gpu_stop_atom(kbdev, js, |
| katom_idx0, |
| action); |
| ret = true; |
| } else if (katom_idx1_valid) { |
| /* idx0 has already completed, |
| * stop idx1 if needed |
| */ |
| kbase_gpu_stop_atom(kbdev, js, |
| katom_idx1, |
| action); |
| ret = true; |
| } |
| } |
| } else if (katom_idx1_valid) { |
| /* idx1 not on GPU but must be dequeued*/ |
| |
| /* idx1 will be handled in IRQ */ |
| kbase_gpu_remove_atom(kbdev, katom_idx1, action, |
| false); |
| /* stop idx0 */ |
| /* This will be repeated for anything removed |
| * from the next registers, since their normal |
| * flow was also interrupted, and this function |
| * might not enter disjoint state e.g. if we |
| * don't actually do a hard stop on the head |
| * atom |
| */ |
| kbase_gpu_stop_atom(kbdev, js, katom_idx0, |
| action); |
| ret = true; |
| } else { |
| /* no atom in idx1 */ |
| /* just stop idx0 */ |
| kbase_gpu_stop_atom(kbdev, js, katom_idx0, |
| action); |
| ret = true; |
| } |
| } |
| } else if (katom_idx1_valid) { |
| if (katom_idx1->gpu_rb_state != KBASE_ATOM_GPU_RB_SUBMITTED) { |
| /* Mark for return */ |
| /* idx1 will be returned once idx0 completes */ |
| kbase_gpu_remove_atom(kbdev, katom_idx1, action, |
| false); |
| } else { |
| /* idx1 is on GPU */ |
| if (kbase_reg_read(kbdev, JOB_SLOT_REG(js, |
| JS_COMMAND_NEXT)) == 0) { |
| /* idx0 has already completed - stop idx1 */ |
| kbase_gpu_stop_atom(kbdev, js, katom_idx1, |
| action); |
| ret = true; |
| } else { |
| /* idx1 is in NEXT registers - attempt to |
| * remove |
| */ |
| kbase_reg_write(kbdev, JOB_SLOT_REG(js, |
| JS_COMMAND_NEXT), |
| JS_COMMAND_NOP); |
| |
| if (kbase_reg_read(kbdev, JOB_SLOT_REG(js, |
| JS_HEAD_NEXT_LO)) != 0 || |
| kbase_reg_read(kbdev, JOB_SLOT_REG(js, |
| JS_HEAD_NEXT_HI)) != 0) { |
| /* idx1 removed successfully, will be |
| * handled in IRQ once idx0 completes |
| */ |
| kbase_gpu_remove_atom(kbdev, katom_idx1, |
| action, |
| false); |
| /* Revert the last_context, or mark as purged */ |
| kbdev->hwaccess.backend.slot_rb[js].last_kctx_tagged = |
| kctx_idx0 ? SLOT_RB_TAG_KCTX(katom_idx0->kctx) : |
| SLOT_RB_TAG_PURGED; |
| } else { |
| /* idx0 has already completed - stop |
| * idx1 |
| */ |
| kbase_gpu_stop_atom(kbdev, js, |
| katom_idx1, |
| action); |
| ret = true; |
| } |
| } |
| } |
| } |
| |
| |
| if (stop_x_dep_idx0 != -1) |
| kbase_backend_soft_hard_stop_slot(kbdev, kctx, stop_x_dep_idx0, |
| NULL, action); |
| |
| if (stop_x_dep_idx1 != -1) |
| kbase_backend_soft_hard_stop_slot(kbdev, kctx, stop_x_dep_idx1, |
| NULL, action); |
| |
| return ret; |
| } |
| |
| void kbase_backend_cache_clean(struct kbase_device *kbdev, |
| struct kbase_jd_atom *katom) |
| { |
| if (katom->need_cache_flush_cores_retained) { |
| kbase_gpu_start_cache_clean(kbdev, |
| GPU_COMMAND_CACHE_CLN_INV_FULL); |
| kbase_gpu_wait_cache_clean(kbdev); |
| |
| katom->need_cache_flush_cores_retained = false; |
| } |
| } |
| |
| void kbase_backend_complete_wq(struct kbase_device *kbdev, |
| struct kbase_jd_atom *katom) |
| { |
| /* |
| * If cache flush required due to HW workaround then perform the flush |
| * now |
| */ |
| kbase_backend_cache_clean(kbdev, katom); |
| } |
| |
| void kbase_backend_complete_wq_post_sched(struct kbase_device *kbdev, |
| base_jd_core_req core_req) |
| { |
| if (!kbdev->pm.active_count) { |
| kbase_pm_lock(kbdev); |
| kbase_pm_update_active(kbdev); |
| kbase_pm_unlock(kbdev); |
| } |
| } |
| |
| void kbase_gpu_dump_slots(struct kbase_device *kbdev) |
| { |
| unsigned long flags; |
| int js; |
| |
| spin_lock_irqsave(&kbdev->hwaccess_lock, flags); |
| |
| dev_info(kbdev->dev, "%s:\n", __func__); |
| |
| for (js = 0; js < kbdev->gpu_props.num_job_slots; js++) { |
| int idx; |
| |
| for (idx = 0; idx < SLOT_RB_SIZE; idx++) { |
| struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, |
| js, |
| idx); |
| |
| if (katom) |
| dev_info(kbdev->dev, |
| " js%d idx%d : katom=%pK gpu_rb_state=%d\n", |
| js, idx, katom, katom->gpu_rb_state); |
| else |
| dev_info(kbdev->dev, " js%d idx%d : empty\n", |
| js, idx); |
| } |
| } |
| |
| spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags); |
| } |
| |
| void kbase_backend_slot_kctx_purge_locked(struct kbase_device *kbdev, struct kbase_context *kctx) |
| { |
| int js; |
| bool tracked = false; |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| for (js = 0; js < kbdev->gpu_props.num_job_slots; js++) { |
| u64 tagged_kctx = kbdev->hwaccess.backend.slot_rb[js].last_kctx_tagged; |
| |
| if (tagged_kctx == SLOT_RB_TAG_KCTX(kctx)) { |
| /* Marking the slot kctx tracking field is purged */ |
| kbdev->hwaccess.backend.slot_rb[js].last_kctx_tagged = SLOT_RB_TAG_PURGED; |
| tracked = true; |
| } |
| } |
| |
| if (tracked) { |
| /* The context had run some jobs before the purge, other slots |
| * in SLOT_RB_NULL_TAG_VAL condition needs to be marked as |
| * purged as well. |
| */ |
| for (js = 0; js < kbdev->gpu_props.num_job_slots; js++) { |
| if (kbdev->hwaccess.backend.slot_rb[js].last_kctx_tagged == |
| SLOT_RB_NULL_TAG_VAL) |
| kbdev->hwaccess.backend.slot_rb[js].last_kctx_tagged = |
| SLOT_RB_TAG_PURGED; |
| } |
| } |
| } |