| // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note |
| /* |
| * |
| * (C) COPYRIGHT 2014-2021 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 backend context / address space management |
| */ |
| |
| #include <mali_kbase.h> |
| #include <mali_kbase_hwaccess_jm.h> |
| #include <mali_kbase_ctx_sched.h> |
| |
| /** |
| * assign_and_activate_kctx_addr_space - Assign an AS to a context |
| * @kbdev: Kbase device |
| * @kctx: Kbase context |
| * @current_as: Address Space to assign |
| * |
| * Assign an Address Space (AS) to a context, and add the context to the Policy. |
| * |
| * This includes |
| * setting up the global runpool_irq structure and the context on the AS, |
| * Activating the MMU on the AS, |
| * Allowing jobs to be submitted on the AS. |
| * |
| * Context: |
| * kbasep_js_kctx_info.jsctx_mutex held, |
| * kbasep_js_device_data.runpool_mutex held, |
| * AS transaction mutex held, |
| * Runpool IRQ lock held |
| */ |
| static void assign_and_activate_kctx_addr_space(struct kbase_device *kbdev, |
| struct kbase_context *kctx, |
| struct kbase_as *current_as) |
| { |
| struct kbasep_js_device_data *js_devdata = &kbdev->js_data; |
| |
| lockdep_assert_held(&kctx->jctx.sched_info.ctx.jsctx_mutex); |
| lockdep_assert_held(&js_devdata->runpool_mutex); |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| #if !MALI_USE_CSF |
| /* Attribute handling */ |
| kbasep_js_ctx_attr_runpool_retain_ctx(kbdev, kctx); |
| #endif |
| |
| /* Allow it to run jobs */ |
| kbasep_js_set_submit_allowed(js_devdata, kctx); |
| |
| kbase_js_runpool_inc_context_count(kbdev, kctx); |
| } |
| |
| bool kbase_backend_use_ctx_sched(struct kbase_device *kbdev, |
| struct kbase_context *kctx, |
| int js) |
| { |
| int i; |
| |
| if (kbdev->hwaccess.active_kctx[js] == kctx) { |
| /* Context is already active */ |
| return true; |
| } |
| |
| for (i = 0; i < kbdev->nr_hw_address_spaces; i++) { |
| if (kbdev->as_to_kctx[i] == kctx) { |
| /* Context already has ASID - mark as active */ |
| return true; |
| } |
| } |
| |
| /* Context does not have address space assigned */ |
| return false; |
| } |
| |
| void kbase_backend_release_ctx_irq(struct kbase_device *kbdev, |
| struct kbase_context *kctx) |
| { |
| int as_nr = kctx->as_nr; |
| |
| if (as_nr == KBASEP_AS_NR_INVALID) { |
| WARN(1, "Attempting to release context without ASID\n"); |
| return; |
| } |
| |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| if (atomic_read(&kctx->refcount) != 1) { |
| WARN(1, "Attempting to release active ASID\n"); |
| return; |
| } |
| |
| kbasep_js_clear_submit_allowed(&kbdev->js_data, kctx); |
| |
| kbase_ctx_sched_release_ctx(kctx); |
| kbase_js_runpool_dec_context_count(kbdev, kctx); |
| } |
| |
| void kbase_backend_release_ctx_noirq(struct kbase_device *kbdev, |
| struct kbase_context *kctx) |
| { |
| } |
| |
| int kbase_backend_find_and_release_free_address_space( |
| struct kbase_device *kbdev, struct kbase_context *kctx) |
| { |
| struct kbasep_js_device_data *js_devdata; |
| struct kbasep_js_kctx_info *js_kctx_info; |
| unsigned long flags; |
| int i; |
| |
| js_devdata = &kbdev->js_data; |
| js_kctx_info = &kctx->jctx.sched_info; |
| |
| rt_mutex_lock(&js_kctx_info->ctx.jsctx_mutex); |
| mutex_lock(&js_devdata->runpool_mutex); |
| |
| spin_lock_irqsave(&kbdev->hwaccess_lock, flags); |
| |
| for (i = 0; i < kbdev->nr_hw_address_spaces; i++) { |
| struct kbasep_js_kctx_info *as_js_kctx_info; |
| struct kbase_context *as_kctx; |
| |
| as_kctx = kbdev->as_to_kctx[i]; |
| as_js_kctx_info = &as_kctx->jctx.sched_info; |
| |
| /* Don't release privileged or active contexts, or contexts with |
| * jobs running. |
| * Note that a context will have at least 1 reference (which |
| * was previously taken by kbasep_js_schedule_ctx()) until |
| * descheduled. |
| */ |
| if (as_kctx && !kbase_ctx_flag(as_kctx, KCTX_PRIVILEGED) && |
| atomic_read(&as_kctx->refcount) == 1) { |
| if (!kbase_ctx_sched_inc_refcount_nolock(as_kctx)) { |
| WARN(1, "Failed to retain active context\n"); |
| |
| spin_unlock_irqrestore(&kbdev->hwaccess_lock, |
| flags); |
| mutex_unlock(&js_devdata->runpool_mutex); |
| rt_mutex_unlock(&js_kctx_info->ctx.jsctx_mutex); |
| |
| return KBASEP_AS_NR_INVALID; |
| } |
| |
| kbasep_js_clear_submit_allowed(js_devdata, as_kctx); |
| |
| /* Drop and retake locks to take the jsctx_mutex on the |
| * context we're about to release without violating lock |
| * ordering |
| */ |
| spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags); |
| mutex_unlock(&js_devdata->runpool_mutex); |
| rt_mutex_unlock(&js_kctx_info->ctx.jsctx_mutex); |
| |
| |
| /* Release context from address space */ |
| rt_mutex_lock(&as_js_kctx_info->ctx.jsctx_mutex); |
| mutex_lock(&js_devdata->runpool_mutex); |
| |
| kbasep_js_runpool_release_ctx_nolock(kbdev, as_kctx); |
| |
| if (!kbase_ctx_flag(as_kctx, KCTX_SCHEDULED)) { |
| kbasep_js_runpool_requeue_or_kill_ctx(kbdev, |
| as_kctx, |
| true); |
| |
| mutex_unlock(&js_devdata->runpool_mutex); |
| rt_mutex_unlock(&as_js_kctx_info->ctx.jsctx_mutex); |
| |
| return i; |
| } |
| |
| /* Context was retained while locks were dropped, |
| * continue looking for free AS |
| */ |
| mutex_unlock(&js_devdata->runpool_mutex); |
| rt_mutex_unlock(&as_js_kctx_info->ctx.jsctx_mutex); |
| |
| rt_mutex_lock(&js_kctx_info->ctx.jsctx_mutex); |
| mutex_lock(&js_devdata->runpool_mutex); |
| spin_lock_irqsave(&kbdev->hwaccess_lock, flags); |
| } |
| } |
| |
| spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags); |
| |
| mutex_unlock(&js_devdata->runpool_mutex); |
| rt_mutex_unlock(&js_kctx_info->ctx.jsctx_mutex); |
| |
| return KBASEP_AS_NR_INVALID; |
| } |
| |
| bool kbase_backend_use_ctx(struct kbase_device *kbdev, |
| struct kbase_context *kctx, |
| int as_nr) |
| { |
| struct kbasep_js_device_data *js_devdata; |
| struct kbase_as *new_address_space = NULL; |
| int js; |
| |
| js_devdata = &kbdev->js_data; |
| |
| for (js = 0; js < BASE_JM_MAX_NR_SLOTS; js++) { |
| if (kbdev->hwaccess.active_kctx[js] == kctx) { |
| WARN(1, "Context is already scheduled in\n"); |
| return false; |
| } |
| } |
| |
| new_address_space = &kbdev->as[as_nr]; |
| |
| lockdep_assert_held(&js_devdata->runpool_mutex); |
| lockdep_assert_held(&kbdev->mmu_hw_mutex); |
| lockdep_assert_held(&kbdev->hwaccess_lock); |
| |
| assign_and_activate_kctx_addr_space(kbdev, kctx, new_address_space); |
| |
| if (kbase_ctx_flag(kctx, KCTX_PRIVILEGED)) { |
| /* We need to retain it to keep the corresponding address space |
| */ |
| kbase_ctx_sched_retain_ctx_refcount(kctx); |
| } |
| |
| return true; |
| } |
| |