Project import generated by Copybara.

GitOrigin-RevId: 98383fa31a2fd6c1b306cf78cb369c0be4694ece
Change-Id: I7621b0443f2dacc15c44c856dbd2cf275a3ce020
diff --git a/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_ctx_sched.c b/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_ctx_sched.c
index 41f8c9c..c5fa78b 100644
--- a/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_ctx_sched.c
+++ b/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_ctx_sched.c
@@ -209,7 +209,17 @@
 
 	mutex_lock(&kbdev->mmu_hw_mutex);
 	spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
+	kbase_ctx_sched_remove_ctx_nolock(kctx);
+	spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
+	mutex_unlock(&kbdev->mmu_hw_mutex);
+}
 
+void kbase_ctx_sched_remove_ctx_nolock(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 >= 0) && (kctx->as_nr < BASE_MAX_NR_AS)) {
@@ -220,9 +230,6 @@
 		kbdev->as_to_kctx[kctx->as_nr] = NULL;
 		kctx->as_nr = KBASEP_AS_NR_INVALID;
 	}
-
-	spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
-	mutex_unlock(&kbdev->mmu_hw_mutex);
 }
 
 void kbase_ctx_sched_restore_all_as(struct kbase_device *kbdev)
diff --git a/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_ctx_sched.h b/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_ctx_sched.h
index b66850f..d888888 100644
--- a/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_ctx_sched.h
+++ b/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_ctx_sched.h
@@ -131,6 +131,20 @@
 void kbase_ctx_sched_remove_ctx(struct kbase_context *kctx);
 
 /**
+ * kbase_ctx_sched_remove_ctx_nolock - Unassign previously assigned address space
+ * @kctx: The context to be removed
+ *
+ * The following lock must be held by the caller:
+ * kbase_device::mmu_hw_mutex
+ * kbase_device::hwaccess_lock
+ *
+ * This function should be called when a context is being destroyed. The
+ * context must no longer have any reference. If it has been assigned an
+ * address space before then the AS will be unprogrammed.
+ */
+void kbase_ctx_sched_remove_ctx_nolock(struct kbase_context *kctx);
+
+/**
  * kbase_ctx_sched_restore_all_as - Reprogram all address spaces
  * @kbdev: The device for which address spaces to be reprogrammed
  *
diff --git a/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_js.c b/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_js.c
index 9b1bd71..3887270 100644
--- a/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_js.c
+++ b/bifrost/r47p0/kernel/drivers/gpu/arm/midgard/mali_kbase_js.c
@@ -796,7 +796,55 @@
 		mutex_unlock(&kbdev->js_data.runpool_mutex);
 	}
 
-	kbase_ctx_sched_remove_ctx(kctx);
+	/* A work item to handle page_fault/bus_fault/gpu_fault could be
+	 * pending for the outgoing context which we no longer care about.
+	 * Ensure that the context won't be accessed anymore by the fault
+	 * workers.
+	 */
+	while (true) {
+		unsigned long flags;
+		int refcount;
+
+		mutex_lock(&kbdev->mmu_hw_mutex);
+		spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
+		refcount = atomic_read(&kctx->refcount);
+		if ((refcount != 0) && !WARN_ON_ONCE(kctx->as_nr == KBASEP_AS_NR_INVALID)) {
+			struct kbase_as *as = &kctx->kbdev->as[kctx->as_nr];
+			int new_refcount;
+
+			dev_dbg(kbdev->dev,
+				"Waiting for pending fault worker to complete when terminating context (%d_%d)",
+				kctx->tgid, kctx->id);
+			spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
+			mutex_unlock(&kbdev->mmu_hw_mutex);
+			flush_workqueue(as->pf_wq);
+			new_refcount = atomic_read(&kctx->refcount);
+			if (refcount != new_refcount) {
+				/* Fault workers executed and released some references, re-check */
+				continue;
+			} else {
+				/* Waiting for pending fault workers to execute was not effective,
+				 * we're going to forcefully de-assign the AS from this context
+				 * because nothing else should still be accessing the context at
+				 * this point.
+				 *
+				 * This should never happen and a WARN_ON() would be printed by
+				 * kbase_ctx_sched_remove_ctx() if the refcount is non-zero.
+				 */
+				dev_warn(
+					kbdev->dev,
+					"No fault workers executed, %d refs remain for terminating context (%d_%d)",
+					new_refcount, kctx->tgid, kctx->id);
+				kbase_ctx_sched_remove_ctx(kctx);
+			}
+		} else {
+			kbase_ctx_sched_remove_ctx_nolock(kctx);
+			spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
+			mutex_unlock(&kbdev->mmu_hw_mutex);
+		}
+		break;
+	}
+
 #if IS_ENABLED(CONFIG_MALI_TRACE_POWER_GPU_WORK_PERIOD)
 	gpu_metrics_ctx_term(kctx);
 #endif