209 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *
 | |
|  * (C) COPYRIGHT 2017 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_kbase.h>
 | |
| #include <mali_kbase_config_defaults.h>
 | |
| 
 | |
| #include "mali_kbase_ctx_sched.h"
 | |
| 
 | |
| int kbase_ctx_sched_init(struct kbase_device *kbdev)
 | |
| {
 | |
| 	int as_present = (1U << kbdev->nr_hw_address_spaces) - 1;
 | |
| 
 | |
| 	/* These two must be recalculated if nr_hw_address_spaces changes
 | |
| 	 * (e.g. for HW workarounds) */
 | |
| 	kbdev->nr_user_address_spaces = kbdev->nr_hw_address_spaces;
 | |
| 	if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8987)) {
 | |
| 		bool use_workaround;
 | |
| 
 | |
| 		use_workaround = DEFAULT_SECURE_BUT_LOSS_OF_PERFORMANCE;
 | |
| 		if (use_workaround) {
 | |
| 			dev_dbg(kbdev->dev, "GPU has HW ISSUE 8987, and driver configured for security workaround: 1 address space only");
 | |
| 			kbdev->nr_user_address_spaces = 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	kbdev->as_free = as_present; /* All ASs initially free */
 | |
| 
 | |
| 	memset(kbdev->as_to_kctx, 0, sizeof(kbdev->as_to_kctx));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void kbase_ctx_sched_term(struct kbase_device *kbdev)
 | |
| {
 | |
| 	s8 i;
 | |
| 
 | |
| 	/* Sanity checks */
 | |
| 	for (i = 0; i != kbdev->nr_hw_address_spaces; ++i) {
 | |
| 		WARN_ON(kbdev->as_to_kctx[i] != NULL);
 | |
| 		WARN_ON(!(kbdev->as_free & (1u << i)));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* kbasep_ctx_sched_find_as_for_ctx - Find a free address space
 | |
|  *
 | |
|  * @kbdev: The context for which to find a free address space
 | |
|  *
 | |
|  * Return: A valid AS if successful, otherwise KBASEP_AS_NR_INVALID
 | |
|  *
 | |
|  * This function returns an address space available for use. It would prefer
 | |
|  * returning an AS that has been previously assigned to the context to
 | |
|  * avoid having to reprogram the MMU.
 | |
|  */
 | |
| static int kbasep_ctx_sched_find_as_for_ctx(struct kbase_context *kctx)
 | |
| {
 | |
| 	struct kbase_device *const kbdev = kctx->kbdev;
 | |
| 	int free_as;
 | |
| 
 | |
| 	lockdep_assert_held(&kbdev->hwaccess_lock);
 | |
| 
 | |
| 	/* First check if the previously assigned AS is available */
 | |
| 	if ((kctx->as_nr != KBASEP_AS_NR_INVALID) &&
 | |
| 			(kbdev->as_free & (1u << kctx->as_nr)))
 | |
| 		return kctx->as_nr;
 | |
| 
 | |
| 	/* The previously assigned AS was taken, we'll be returning any free
 | |
| 	 * AS at this point.
 | |
| 	 */
 | |
| 	free_as = ffs(kbdev->as_free) - 1;
 | |
| 	if (free_as >= 0 && free_as < kbdev->nr_hw_address_spaces)
 | |
| 		return free_as;
 | |
| 
 | |
| 	return KBASEP_AS_NR_INVALID;
 | |
| }
 | |
| 
 | |
| int kbase_ctx_sched_retain_ctx(struct kbase_context *kctx)
 | |
| {
 | |
| 	struct kbase_device *const kbdev = kctx->kbdev;
 | |
| 
 | |
| 	lockdep_assert_held(&kbdev->mmu_hw_mutex);
 | |
| 	lockdep_assert_held(&kbdev->hwaccess_lock);
 | |
| 
 | |
| 	WARN_ON(!kbdev->pm.backend.gpu_powered);
 | |
| 
 | |
| 	if (atomic_inc_return(&kctx->refcount) == 1) {
 | |
| 		int const free_as = kbasep_ctx_sched_find_as_for_ctx(kctx);
 | |
| 
 | |
| 		if (free_as != KBASEP_AS_NR_INVALID) {
 | |
| 			kbdev->as_free &= ~(1u << free_as);
 | |
| 			/* Only program the MMU if the context has not been
 | |
| 			 * assigned the same address space before.
 | |
| 			 */
 | |
| 			if (free_as != kctx->as_nr) {
 | |
| 				struct kbase_context *const prev_kctx =
 | |
| 					kbdev->as_to_kctx[free_as];
 | |
| 
 | |
| 				if (prev_kctx) {
 | |
| 					WARN_ON(atomic_read(&prev_kctx->refcount) != 0);
 | |
| 					kbase_mmu_disable(prev_kctx);
 | |
| 					prev_kctx->as_nr = KBASEP_AS_NR_INVALID;
 | |
| 				}
 | |
| 
 | |
| 				kctx->as_nr = free_as;
 | |
| 				kbdev->as_to_kctx[free_as] = kctx;
 | |
| 				kbase_mmu_update(kctx);
 | |
| 			}
 | |
| 		} else {
 | |
| 			atomic_dec(&kctx->refcount);
 | |
| 
 | |
| 			/* Failed to find an available address space, we must
 | |
| 			 * be returning an error at this point.
 | |
| 			 */
 | |
| 			WARN_ON(kctx->as_nr != KBASEP_AS_NR_INVALID);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return kctx->as_nr;
 | |
| }
 | |
| 
 | |
| int kbase_ctx_sched_retain_ctx_refcount(struct kbase_context *kctx)
 | |
| {
 | |
| 	struct kbase_device *const kbdev = kctx->kbdev;
 | |
| 
 | |
| 	lockdep_assert_held(&kbdev->hwaccess_lock);
 | |
| 	WARN_ON(atomic_read(&kctx->refcount) == 0);
 | |
| 	if (atomic_read(&kctx->refcount) == 0)
 | |
| 		return -1;
 | |
| 
 | |
| 	WARN_ON(kctx->as_nr == KBASEP_AS_NR_INVALID);
 | |
| 	WARN_ON(kbdev->as_to_kctx[kctx->as_nr] != kctx);
 | |
| 
 | |
| 	atomic_inc(&kctx->refcount);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void kbase_ctx_sched_release_ctx(struct kbase_context *kctx)
 | |
| {
 | |
| 	struct kbase_device *const kbdev = kctx->kbdev;
 | |
| 
 | |
| 	lockdep_assert_held(&kbdev->hwaccess_lock);
 | |
| 
 | |
| 	if (atomic_dec_return(&kctx->refcount) == 0)
 | |
| 		kbdev->as_free |= (1u << kctx->as_nr);
 | |
| }
 | |
| 
 | |
| void kbase_ctx_sched_remove_ctx(struct kbase_context *kctx)
 | |
| {
 | |
| 	struct kbase_device *const kbdev = kctx->kbdev;
 | |
| 
 | |
| 	lockdep_assert_held(&kbdev->mmu_hw_mutex);
 | |
| 	lockdep_assert_held(&kbdev->hwaccess_lock);
 | |
| 
 | |
| 	WARN_ON(atomic_read(&kctx->refcount) != 0);
 | |
| 
 | |
| 	if (kctx->as_nr != KBASEP_AS_NR_INVALID) {
 | |
| 		if (kbdev->pm.backend.gpu_powered)
 | |
| 			kbase_mmu_disable(kctx);
 | |
| 
 | |
| 		kbdev->as_to_kctx[kctx->as_nr] = NULL;
 | |
| 		kctx->as_nr = KBASEP_AS_NR_INVALID;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void kbase_ctx_sched_restore_all_as(struct kbase_device *kbdev)
 | |
| {
 | |
| 	s8 i;
 | |
| 
 | |
| 	lockdep_assert_held(&kbdev->mmu_hw_mutex);
 | |
| 	lockdep_assert_held(&kbdev->hwaccess_lock);
 | |
| 
 | |
| 	WARN_ON(!kbdev->pm.backend.gpu_powered);
 | |
| 
 | |
| 	for (i = 0; i != kbdev->nr_hw_address_spaces; ++i) {
 | |
| 		struct kbase_context *kctx;
 | |
| 
 | |
| 		kctx = kbdev->as_to_kctx[i];
 | |
| 		if (kctx) {
 | |
| 			if (atomic_read(&kctx->refcount)) {
 | |
| 				WARN_ON(kctx->as_nr != i);
 | |
| 
 | |
| 				kbase_mmu_update(kctx);
 | |
| 			} else {
 | |
| 				/* This context might have been assigned an
 | |
| 				 * AS before, clear it.
 | |
| 				 */
 | |
| 				kbdev->as_to_kctx[kctx->as_nr] = NULL;
 | |
| 				kctx->as_nr = KBASEP_AS_NR_INVALID;
 | |
| 			}
 | |
| 		} else {
 | |
| 			kbase_mmu_disable_as(kbdev, i);
 | |
| 		}
 | |
| 	}
 | |
| }
 |