summaryrefslogtreecommitdiff
path: root/mali_kbase/csf/mali_kbase_csf_scheduler.c
diff options
context:
space:
mode:
Diffstat (limited to 'mali_kbase/csf/mali_kbase_csf_scheduler.c')
-rw-r--r--mali_kbase/csf/mali_kbase_csf_scheduler.c368
1 files changed, 264 insertions, 104 deletions
diff --git a/mali_kbase/csf/mali_kbase_csf_scheduler.c b/mali_kbase/csf/mali_kbase_csf_scheduler.c
index 22a08a6..f1ff8bf 100644
--- a/mali_kbase/csf/mali_kbase_csf_scheduler.c
+++ b/mali_kbase/csf/mali_kbase_csf_scheduler.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/*
*
- * (C) COPYRIGHT 2018-2023 ARM Limited. All rights reserved.
+ * (C) COPYRIGHT 2018-2024 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
@@ -39,6 +39,7 @@
#include "mali_kbase_csf_tiler_heap_reclaim.h"
#include "mali_kbase_csf_mcu_shared_reg.h"
#include <linux/version_compat_defs.h>
+#include <hwcnt/mali_kbase_hwcnt_context.h>
#if IS_ENABLED(CONFIG_MALI_TRACE_POWER_GPU_WORK_PERIOD)
#include <mali_kbase_gpu_metrics.h>
#include <csf/mali_kbase_csf_trace_buffer.h>
@@ -91,7 +92,8 @@ scheduler_get_protm_enter_async_group(struct kbase_device *const kbdev,
struct kbase_queue_group *const group);
static struct kbase_queue_group *get_tock_top_group(struct kbase_csf_scheduler *const scheduler);
static void scheduler_enable_tick_timer_nolock(struct kbase_device *kbdev);
-static int suspend_active_queue_groups(struct kbase_device *kbdev, unsigned long *slot_mask);
+static int suspend_active_queue_groups(struct kbase_device *kbdev, unsigned long *slot_mask,
+ bool reset);
static int suspend_active_groups_on_powerdown(struct kbase_device *kbdev, bool system_suspend);
static void schedule_in_cycle(struct kbase_queue_group *group, bool force);
static bool queue_group_scheduled_locked(struct kbase_queue_group *group);
@@ -795,7 +797,8 @@ static void update_on_slot_queues_offsets(struct kbase_device *kbdev)
static void enqueue_gpu_idle_work(struct kbase_csf_scheduler *const scheduler)
{
atomic_set(&scheduler->gpu_no_longer_idle, false);
- queue_work(scheduler->idle_wq, &scheduler->gpu_idle_work);
+ atomic_inc(&scheduler->pending_gpu_idle_work);
+ complete(&scheduler->kthread_signal);
}
bool kbase_csf_scheduler_process_gpu_idle_event(struct kbase_device *kbdev)
@@ -807,7 +810,8 @@ bool kbase_csf_scheduler_process_gpu_idle_event(struct kbase_device *kbdev)
lockdep_assert_held(&kbdev->hwaccess_lock);
lockdep_assert_held(&scheduler->interrupt_lock);
- can_suspend_on_idle = kbase_pm_idle_groups_sched_suspendable(kbdev);
+ can_suspend_on_idle = kbase_pm_idle_groups_sched_suspendable(kbdev) &&
+ !kbase_pm_is_mcu_inactive(kbdev, kbdev->pm.backend.mcu_state);
KBASE_KTRACE_ADD(kbdev, SCHEDULER_GPU_IDLE_EVENT_CAN_SUSPEND, NULL,
(((u64)can_suspend_on_idle) << 32));
@@ -2423,6 +2427,11 @@ static void cancel_tock_work(struct kbase_csf_scheduler *const scheduler)
atomic_set(&scheduler->pending_tock_work, false);
}
+static void cancel_gpu_idle_work(struct kbase_csf_scheduler *const scheduler)
+{
+ atomic_set(&scheduler->pending_gpu_idle_work, false);
+}
+
static void remove_group_from_runnable(struct kbase_csf_scheduler *const scheduler,
struct kbase_queue_group *group,
enum kbase_csf_group_state run_state)
@@ -3148,8 +3157,9 @@ static void sched_evict_group(struct kbase_queue_group *group, bool fault,
static int term_group_sync(struct kbase_queue_group *group)
{
struct kbase_device *kbdev = group->kctx->kbdev;
- const unsigned int fw_timeout_ms = kbase_get_timeout_ms(kbdev, CSF_FIRMWARE_TIMEOUT);
- long remaining = kbase_csf_timeout_in_jiffies(fw_timeout_ms);
+ const unsigned int group_term_timeout_ms =
+ kbase_get_timeout_ms(kbdev, CSF_CSG_TERM_TIMEOUT);
+ long remaining = kbase_csf_timeout_in_jiffies(group_term_timeout_ms);
int err = 0;
term_csg_slot(group);
@@ -3166,7 +3176,7 @@ static int term_group_sync(struct kbase_queue_group *group)
dev_warn(
kbdev->dev,
"[%llu] term request timeout (%d ms) for group %d of context %d_%d on slot %d",
- kbase_backend_get_cycle_cnt(kbdev), fw_timeout_ms, group->handle,
+ kbase_backend_get_cycle_cnt(kbdev), group_term_timeout_ms, group->handle,
group->kctx->tgid, group->kctx->id, group->csg_nr);
if (kbase_csf_firmware_ping_wait(kbdev, FW_PING_AFTER_ERROR_TIMEOUT_MS))
error_type = DF_PING_REQUEST_TIMEOUT;
@@ -4824,8 +4834,9 @@ static int suspend_active_groups_on_powerdown(struct kbase_device *kbdev, bool s
{
struct kbase_csf_scheduler *const scheduler = &kbdev->csf.scheduler;
DECLARE_BITMAP(slot_mask, MAX_SUPPORTED_CSGS) = { 0 };
+ int ret;
- int ret = suspend_active_queue_groups(kbdev, slot_mask);
+ ret = suspend_active_queue_groups(kbdev, slot_mask, false);
if (unlikely(ret)) {
const int csg_nr = ffs(slot_mask[0]) - 1;
@@ -5016,14 +5027,14 @@ static bool scheduler_suspend_on_idle(struct kbase_device *kbdev)
return true;
}
-static void gpu_idle_worker(struct work_struct *work)
+static void gpu_idle_worker(struct kbase_device *kbdev)
{
- struct kbase_device *kbdev =
- container_of(work, struct kbase_device, csf.scheduler.gpu_idle_work);
struct kbase_csf_scheduler *const scheduler = &kbdev->csf.scheduler;
bool scheduler_is_idle_suspendable = false;
bool all_groups_suspended = false;
+ WARN_ON_ONCE(atomic_read(&scheduler->pending_gpu_idle_work) == 0);
+
KBASE_KTRACE_ADD(kbdev, SCHEDULER_GPU_IDLE_WORKER_START, NULL, 0u);
#define __ENCODE_KTRACE_INFO(reset, idle, all_suspend) \
@@ -5033,7 +5044,7 @@ static void gpu_idle_worker(struct work_struct *work)
dev_warn(kbdev->dev, "Quit idle for failing to prevent gpu reset.\n");
KBASE_KTRACE_ADD(kbdev, SCHEDULER_GPU_IDLE_WORKER_END, NULL,
__ENCODE_KTRACE_INFO(true, false, false));
- return;
+ goto exit;
}
kbase_debug_csf_fault_wait_completion(kbdev);
rt_mutex_lock(&scheduler->lock);
@@ -5042,7 +5053,7 @@ static void gpu_idle_worker(struct work_struct *work)
if (unlikely(scheduler->state == SCHED_BUSY)) {
rt_mutex_unlock(&scheduler->lock);
kbase_reset_gpu_allow(kbdev);
- return;
+ goto exit;
}
#endif
@@ -5067,6 +5078,9 @@ static void gpu_idle_worker(struct work_struct *work)
__ENCODE_KTRACE_INFO(false, scheduler_is_idle_suspendable,
all_groups_suspended));
#undef __ENCODE_KTRACE_INFO
+
+exit:
+ atomic_dec(&scheduler->pending_gpu_idle_work);
}
static int scheduler_prepare(struct kbase_device *kbdev)
@@ -5691,7 +5705,9 @@ exit_no_schedule_unlock:
kbase_reset_gpu_allow(kbdev);
}
-static int suspend_active_queue_groups(struct kbase_device *kbdev, unsigned long *slot_mask)
+
+static int suspend_active_queue_groups(struct kbase_device *kbdev, unsigned long *slot_mask,
+ bool reset)
{
struct kbase_csf_scheduler *const scheduler = &kbdev->csf.scheduler;
u32 num_groups = kbdev->csf.global_iface.group_num;
@@ -5704,12 +5720,12 @@ static int suspend_active_queue_groups(struct kbase_device *kbdev, unsigned long
struct kbase_queue_group *group = scheduler->csg_slots[slot_num].resident_group;
if (group) {
- suspend_queue_group(group);
+ suspend_queue_group(group);
set_bit(slot_num, slot_mask);
}
}
- ret = wait_csg_slots_suspend(kbdev, slot_mask);
+ ret = wait_csg_slots_suspend(kbdev, slot_mask);
return ret;
}
@@ -5722,7 +5738,7 @@ static int suspend_active_queue_groups_on_reset(struct kbase_device *kbdev)
rt_mutex_lock(&scheduler->lock);
- ret = suspend_active_queue_groups(kbdev, slot_mask);
+ ret = suspend_active_queue_groups(kbdev, slot_mask, true);
if (ret) {
dev_warn(
@@ -5864,9 +5880,9 @@ static void scheduler_inner_reset(struct kbase_device *kbdev)
WARN_ON(kbase_csf_scheduler_get_nr_active_csgs(kbdev));
/* Cancel any potential queued delayed work(s) */
- cancel_work_sync(&kbdev->csf.scheduler.gpu_idle_work);
cancel_tick_work(scheduler);
cancel_tock_work(scheduler);
+ cancel_gpu_idle_work(scheduler);
cancel_delayed_work_sync(&scheduler->ping_work);
rt_mutex_lock(&scheduler->lock);
@@ -5894,12 +5910,13 @@ static void scheduler_inner_reset(struct kbase_device *kbdev)
void kbase_csf_scheduler_reset(struct kbase_device *kbdev)
{
struct kbase_context *kctx;
-
WARN_ON(!kbase_reset_gpu_is_active(kbdev));
KBASE_KTRACE_ADD(kbdev, SCHEDULER_RESET_START, NULL, 0u);
- kbase_debug_csf_fault_wait_completion(kbdev);
+ if (kbase_reset_gpu_is_active(kbdev))
+ kbase_debug_csf_fault_wait_completion(kbdev);
+
if (scheduler_handle_reset_in_protected_mode(kbdev) &&
!suspend_active_queue_groups_on_reset(kbdev)) {
@@ -6488,8 +6505,8 @@ static void check_sync_update_in_sleep_mode(struct kbase_device *kbdev)
* check_group_sync_update_worker() - Check the sync wait condition for all the
* blocked queue groups
*
- * @work: Pointer to the context-specific work item for evaluating the wait
- * condition for all the queue groups in idle_wait_groups list.
+ * @kctx: The context to evaluate the wait condition for all the queue groups
+ * in idle_wait_groups list.
*
* This function checks the gpu queues of all the groups present in both
* idle_wait_groups list of a context and all on slot idle groups (if GPU
@@ -6499,25 +6516,14 @@ static void check_sync_update_in_sleep_mode(struct kbase_device *kbdev)
* runnable groups so that Scheduler can consider scheduling the group
* in next tick or exit protected mode.
*/
-static void check_group_sync_update_worker(struct kthread_work *work)
+static void check_group_sync_update_worker(struct kbase_context *kctx)
{
- struct kbase_context *const kctx =
- container_of(work, struct kbase_context, csf.sched.sync_update_work);
struct kbase_device *const kbdev = kctx->kbdev;
struct kbase_csf_scheduler *const scheduler = &kbdev->csf.scheduler;
bool sync_updated = false;
rt_mutex_lock(&scheduler->lock);
-#if IS_ENABLED(CONFIG_DEBUG_FS)
- if (unlikely(scheduler->state == SCHED_BUSY)) {
- kthread_queue_work(&kctx->csf.sched.sync_update_worker,
- &kctx->csf.sched.sync_update_work);
- rt_mutex_unlock(&scheduler->lock);
- return;
- }
-#endif
-
KBASE_KTRACE_ADD(kbdev, SCHEDULER_GROUP_SYNC_UPDATE_WORKER_START, kctx, 0u);
if (kctx->csf.sched.num_idle_wait_grps != 0) {
struct kbase_queue_group *group, *temp;
@@ -6558,8 +6564,7 @@ static enum kbase_csf_event_callback_action check_group_sync_update_cb(void *par
KBASE_KTRACE_ADD(kctx->kbdev, SCHEDULER_GROUP_SYNC_UPDATE_EVENT, kctx, 0u);
- kthread_queue_work(&kctx->csf.sched.sync_update_worker,
- &kctx->csf.sched.sync_update_work);
+ kbase_csf_scheduler_enqueue_sync_update_work(kctx);
return KBASE_CSF_EVENT_CALLBACK_KEEP;
}
@@ -6570,6 +6575,8 @@ int kbase_csf_scheduler_context_init(struct kbase_context *kctx)
int err;
struct kbase_device *kbdev = kctx->kbdev;
+ WARN_ON_ONCE(!kbdev->csf.scheduler.kthread_running);
+
#if IS_ENABLED(CONFIG_MALI_TRACE_POWER_GPU_WORK_PERIOD)
err = gpu_metrics_ctx_init(kctx);
if (err)
@@ -6582,16 +6589,7 @@ int kbase_csf_scheduler_context_init(struct kbase_context *kctx)
INIT_LIST_HEAD(&kctx->csf.sched.idle_wait_groups);
- err = kbase_kthread_run_worker_rt(kctx->kbdev, &kctx->csf.sched.sync_update_worker, "csf_sync_update");
- if (err) {
- dev_err(kctx->kbdev->dev,
- "Failed to initialize scheduler context workqueue");
- err = -ENOMEM;
- goto alloc_wq_failed;
- }
-
- kthread_init_work(&kctx->csf.sched.sync_update_work,
- check_group_sync_update_worker);
+ INIT_LIST_HEAD(&kctx->csf.sched.sync_update_work);
kbase_csf_tiler_heap_reclaim_ctx_init(kctx);
@@ -6605,8 +6603,6 @@ int kbase_csf_scheduler_context_init(struct kbase_context *kctx)
return err;
event_wait_add_failed:
- kbase_destroy_kworker_stack(&kctx->csf.sched.sync_update_worker);
-alloc_wq_failed:
kbase_ctx_sched_remove_ctx(kctx);
#if IS_ENABLED(CONFIG_MALI_TRACE_POWER_GPU_WORK_PERIOD)
gpu_metrics_ctx_term(kctx);
@@ -6617,8 +6613,10 @@ alloc_wq_failed:
void kbase_csf_scheduler_context_term(struct kbase_context *kctx)
{
kbase_csf_event_wait_remove(kctx, check_group_sync_update_cb, kctx);
- kthread_cancel_work_sync(&kctx->csf.sched.sync_update_work);
- kbase_destroy_kworker_stack(&kctx->csf.sched.sync_update_worker);
+
+ /* Drain a pending SYNC_UPDATE work if any */
+ kbase_csf_scheduler_wait_for_kthread_pending_work(kctx->kbdev,
+ &kctx->csf.pending_sync_update);
kbase_ctx_sched_remove_ctx(kctx);
#if IS_ENABLED(CONFIG_MALI_TRACE_POWER_GPU_WORK_PERIOD)
@@ -6626,53 +6624,157 @@ void kbase_csf_scheduler_context_term(struct kbase_context *kctx)
#endif /* CONFIG_MALI_TRACE_POWER_GPU_WORK_PERIOD */
}
+static void handle_pending_sync_update_works(struct kbase_csf_scheduler *scheduler)
+{
+ struct kbase_context *sync_update_ctx;
+
+ if (atomic_cmpxchg(&scheduler->pending_sync_update_works, true, false) == false)
+ return;
+
+ do {
+ unsigned long flags;
+
+ spin_lock_irqsave(&scheduler->sync_update_work_ctxs_lock, flags);
+ sync_update_ctx = NULL;
+ if (!list_empty(&scheduler->sync_update_work_ctxs)) {
+ sync_update_ctx = list_first_entry(&scheduler->sync_update_work_ctxs,
+ struct kbase_context,
+ csf.sched.sync_update_work);
+ list_del_init(&sync_update_ctx->csf.sched.sync_update_work);
+ }
+ spin_unlock_irqrestore(&scheduler->sync_update_work_ctxs_lock, flags);
+
+ if (sync_update_ctx != NULL) {
+ WARN_ON_ONCE(atomic_read(&sync_update_ctx->csf.pending_sync_update) == 0);
+ check_group_sync_update_worker(sync_update_ctx);
+ atomic_dec(&sync_update_ctx->csf.pending_sync_update);
+ }
+ } while (sync_update_ctx != NULL);
+}
+
+static void handle_pending_protm_requests(struct kbase_csf_scheduler *scheduler)
+{
+ struct kbase_queue_group *protm_grp;
+
+ if (atomic_cmpxchg(&scheduler->pending_protm_event_works, true, false) == false)
+ return;
+
+ do {
+ unsigned long flags;
+
+ spin_lock_irqsave(&scheduler->protm_event_work_grps_lock, flags);
+ protm_grp = NULL;
+ if (!list_empty(&scheduler->protm_event_work_grps)) {
+ protm_grp = list_first_entry(&scheduler->protm_event_work_grps,
+ struct kbase_queue_group, protm_event_work);
+ list_del_init(&protm_grp->protm_event_work);
+ }
+ spin_unlock_irqrestore(&scheduler->protm_event_work_grps_lock, flags);
+
+ if (protm_grp != NULL) {
+ WARN_ON_ONCE(atomic_read(&protm_grp->pending_protm_event_work) == 0);
+ kbase_csf_process_protm_event_request(protm_grp);
+ atomic_dec(&protm_grp->pending_protm_event_work);
+ }
+ } while (protm_grp != NULL);
+}
+
+static void handle_pending_kcpuq_commands(struct kbase_csf_scheduler *scheduler)
+{
+ struct kbase_kcpu_command_queue *kcpuq;
+
+ if (atomic_cmpxchg(&scheduler->pending_kcpuq_works, true, false) == false)
+ return;
+
+ do {
+ unsigned long flags;
+
+ spin_lock_irqsave(&scheduler->kcpuq_work_queues_lock, flags);
+ kcpuq = NULL;
+ if (!list_empty(&scheduler->kcpuq_work_queues)) {
+ kcpuq = list_first_entry(&scheduler->kcpuq_work_queues,
+ struct kbase_kcpu_command_queue, high_prio_work);
+ list_del_init(&kcpuq->high_prio_work);
+ }
+ spin_unlock_irqrestore(&scheduler->kcpuq_work_queues_lock, flags);
+
+ if (kcpuq != NULL) {
+ WARN_ON_ONCE(atomic_read(&kcpuq->pending_kick) == 0);
+
+ mutex_lock(&kcpuq->lock);
+ kbase_csf_kcpu_queue_process(kcpuq, false);
+ mutex_unlock(&kcpuq->lock);
+
+ atomic_dec(&kcpuq->pending_kick);
+ }
+ } while (kcpuq != NULL);
+}
+
+static void handle_pending_queue_kicks(struct kbase_device *kbdev)
+{
+ struct kbase_csf_scheduler *const scheduler = &kbdev->csf.scheduler;
+ struct kbase_queue *queue;
+
+ if (atomic_cmpxchg(&kbdev->csf.pending_gpuq_kicks, true, false) == false)
+ return;
+
+ do {
+ u8 prio;
+
+ spin_lock(&kbdev->csf.pending_gpuq_kick_queues_lock);
+ queue = NULL;
+ for (prio = 0; prio != KBASE_QUEUE_GROUP_PRIORITY_COUNT; ++prio) {
+ if (!list_empty(&kbdev->csf.pending_gpuq_kick_queues[prio])) {
+ queue = list_first_entry(&kbdev->csf.pending_gpuq_kick_queues[prio],
+ struct kbase_queue, pending_kick_link);
+ list_del_init(&queue->pending_kick_link);
+ break;
+ }
+ }
+ spin_unlock(&kbdev->csf.pending_gpuq_kick_queues_lock);
+
+ if (queue != NULL) {
+ WARN_ONCE(
+ prio != queue->group_priority,
+ "Queue %pK has priority %u but instead its kick was handled at priority %u",
+ (void *)queue, queue->group_priority, prio);
+ WARN_ON_ONCE(atomic_read(&queue->pending_kick) == 0);
+
+ kbase_csf_process_queue_kick(queue);
+
+ /* Perform a scheduling tock for high-priority queue groups if
+ * required.
+ */
+ BUILD_BUG_ON(KBASE_QUEUE_GROUP_PRIORITY_REALTIME != 0);
+ BUILD_BUG_ON(KBASE_QUEUE_GROUP_PRIORITY_HIGH != 1);
+ if ((prio <= KBASE_QUEUE_GROUP_PRIORITY_HIGH) &&
+ atomic_read(&scheduler->pending_tock_work))
+ schedule_on_tock(kbdev);
+ }
+ } while (queue != NULL);
+}
+
static int kbase_csf_scheduler_kthread(void *data)
{
struct kbase_device *const kbdev = data;
struct kbase_csf_scheduler *const scheduler = &kbdev->csf.scheduler;
while (scheduler->kthread_running) {
- struct kbase_queue *queue;
-
if (wait_for_completion_interruptible(&scheduler->kthread_signal) != 0)
continue;
reinit_completion(&scheduler->kthread_signal);
- /* Iterate through queues with pending kicks */
- do {
- u8 prio;
-
- spin_lock(&kbdev->csf.pending_gpuq_kicks_lock);
- queue = NULL;
- for (prio = 0; prio != KBASE_QUEUE_GROUP_PRIORITY_COUNT; ++prio) {
- if (!list_empty(&kbdev->csf.pending_gpuq_kicks[prio])) {
- queue = list_first_entry(
- &kbdev->csf.pending_gpuq_kicks[prio],
- struct kbase_queue, pending_kick_link);
- list_del_init(&queue->pending_kick_link);
- break;
- }
- }
- spin_unlock(&kbdev->csf.pending_gpuq_kicks_lock);
-
- if (queue != NULL) {
- WARN_ONCE(
- prio != queue->group_priority,
- "Queue %pK has priority %hhu but instead its kick was handled at priority %hhu",
- (void *)queue, queue->group_priority, prio);
-
- kbase_csf_process_queue_kick(queue);
+ /*
+ * The order in which these requests are handled is based on
+ * how they would influence each other's decisions. As a
+ * result, the tick & tock requests must be handled after all
+ * other requests, but before the GPU IDLE work.
+ */
- /* Perform a scheduling tock for high-priority queue groups if
- * required.
- */
- BUILD_BUG_ON(KBASE_QUEUE_GROUP_PRIORITY_REALTIME != 0);
- BUILD_BUG_ON(KBASE_QUEUE_GROUP_PRIORITY_HIGH != 1);
- if ((prio <= KBASE_QUEUE_GROUP_PRIORITY_HIGH) &&
- atomic_read(&scheduler->pending_tock_work))
- schedule_on_tock(kbdev);
- }
- } while (queue != NULL);
+ handle_pending_sync_update_works(scheduler);
+ handle_pending_protm_requests(scheduler);
+ handle_pending_kcpuq_commands(scheduler);
+ handle_pending_queue_kicks(kbdev);
/* Check if we need to perform a scheduling tick/tock. A tick
* event shall override a tock event but not vice-versa.
@@ -6684,6 +6786,10 @@ static int kbase_csf_scheduler_kthread(void *data)
schedule_on_tock(kbdev);
}
+ /* Drain pending GPU idle works */
+ while (atomic_read(&scheduler->pending_gpu_idle_work) > 0)
+ gpu_idle_worker(kbdev);
+
dev_dbg(kbdev->dev, "Waking up for event after a scheduling iteration.");
wake_up_all(&kbdev->csf.event_wait);
@@ -6756,12 +6862,6 @@ int kbase_csf_scheduler_early_init(struct kbase_device *kbdev)
atomic_set(&scheduler->timer_enabled, true);
- scheduler->idle_wq = alloc_ordered_workqueue("csf_scheduler_gpu_idle_wq", WQ_HIGHPRI);
- if (!scheduler->idle_wq) {
- dev_err(kbdev->dev, "Failed to allocate GPU idle scheduler workqueue\n");
- return -ENOMEM;
- }
-
INIT_DEFERRABLE_WORK(&scheduler->ping_work, firmware_aliveness_monitor);
rt_mutex_init(&scheduler->lock);
@@ -6779,18 +6879,23 @@ int kbase_csf_scheduler_early_init(struct kbase_device *kbdev)
KBASE_KTRACE_ADD(kbdev, SCHED_SUSPENDED, NULL, scheduler->state);
scheduler->csg_scheduling_period_ms = CSF_SCHEDULER_TIME_TICK_MS;
scheduler_doorbell_init(kbdev);
-
- INIT_WORK(&scheduler->gpu_idle_work, gpu_idle_worker);
- scheduler->fast_gpu_idle_handling = false;
- atomic_set(&scheduler->gpu_no_longer_idle, false);
- atomic_set(&scheduler->non_idle_offslot_grps, 0);
-
hrtimer_init(&scheduler->tick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
scheduler->tick_timer.function = tick_timer_callback;
- kbase_csf_tiler_heap_reclaim_mgr_init(kbdev);
+ atomic_set(&scheduler->pending_sync_update_works, false);
+ spin_lock_init(&scheduler->sync_update_work_ctxs_lock);
+ INIT_LIST_HEAD(&scheduler->sync_update_work_ctxs);
+ atomic_set(&scheduler->pending_protm_event_works, false);
+ spin_lock_init(&scheduler->protm_event_work_grps_lock);
+ INIT_LIST_HEAD(&scheduler->protm_event_work_grps);
+ atomic_set(&scheduler->pending_kcpuq_works, false);
+ spin_lock_init(&scheduler->kcpuq_work_queues_lock);
+ INIT_LIST_HEAD(&scheduler->kcpuq_work_queues);
+ atomic_set(&scheduler->pending_tick_work, false);
+ atomic_set(&scheduler->pending_tock_work, false);
+ atomic_set(&scheduler->pending_gpu_idle_work, 0);
- return 0;
+ return kbase_csf_tiler_heap_reclaim_mgr_init(kbdev);
}
void kbase_csf_scheduler_term(struct kbase_device *kbdev)
@@ -6811,7 +6916,6 @@ void kbase_csf_scheduler_term(struct kbase_device *kbdev)
* to be active at the time of Driver unload.
*/
WARN_ON(kbase_csf_scheduler_get_nr_active_csgs(kbdev));
- flush_work(&kbdev->csf.scheduler.gpu_idle_work);
rt_mutex_lock(&kbdev->csf.scheduler.lock);
if (kbdev->csf.scheduler.state != SCHED_SUSPENDED) {
@@ -6838,9 +6942,6 @@ void kbase_csf_scheduler_term(struct kbase_device *kbdev)
void kbase_csf_scheduler_early_term(struct kbase_device *kbdev)
{
- if (kbdev->csf.scheduler.idle_wq)
- destroy_workqueue(kbdev->csf.scheduler.idle_wq);
-
kbase_csf_tiler_heap_reclaim_mgr_term(kbdev);
}
@@ -7121,6 +7222,65 @@ int kbase_csf_scheduler_handle_runtime_suspend(struct kbase_device *kbdev)
return 0;
}
+void kbase_csf_scheduler_enqueue_sync_update_work(struct kbase_context *kctx)
+{
+ struct kbase_csf_scheduler *const scheduler = &kctx->kbdev->csf.scheduler;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scheduler->sync_update_work_ctxs_lock, flags);
+ if (list_empty(&kctx->csf.sched.sync_update_work)) {
+ list_add_tail(&kctx->csf.sched.sync_update_work, &scheduler->sync_update_work_ctxs);
+ atomic_inc(&kctx->csf.pending_sync_update);
+ if (atomic_cmpxchg(&scheduler->pending_sync_update_works, false, true) == false)
+ complete(&scheduler->kthread_signal);
+ }
+ spin_unlock_irqrestore(&scheduler->sync_update_work_ctxs_lock, flags);
+}
+
+void kbase_csf_scheduler_enqueue_protm_event_work(struct kbase_queue_group *group)
+{
+ struct kbase_context *const kctx = group->kctx;
+ struct kbase_csf_scheduler *const scheduler = &kctx->kbdev->csf.scheduler;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scheduler->protm_event_work_grps_lock, flags);
+ if (list_empty(&group->protm_event_work)) {
+ list_add_tail(&group->protm_event_work, &scheduler->protm_event_work_grps);
+ atomic_inc(&group->pending_protm_event_work);
+ if (atomic_cmpxchg(&scheduler->pending_protm_event_works, false, true) == false)
+ complete(&scheduler->kthread_signal);
+ }
+ spin_unlock_irqrestore(&scheduler->protm_event_work_grps_lock, flags);
+}
+
+void kbase_csf_scheduler_enqueue_kcpuq_work(struct kbase_kcpu_command_queue *queue)
+{
+ struct kbase_csf_scheduler *const scheduler = &queue->kctx->kbdev->csf.scheduler;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scheduler->kcpuq_work_queues_lock, flags);
+ if (list_empty(&queue->high_prio_work)) {
+ list_add_tail(&queue->high_prio_work, &scheduler->kcpuq_work_queues);
+ atomic_inc(&queue->pending_kick);
+ if (atomic_cmpxchg(&scheduler->pending_kcpuq_works, false, true) == false)
+ complete(&scheduler->kthread_signal);
+ }
+ spin_unlock_irqrestore(&scheduler->kcpuq_work_queues_lock, flags);
+}
+
+void kbase_csf_scheduler_wait_for_kthread_pending_work(struct kbase_device *kbdev,
+ atomic_t *pending)
+{
+ /*
+ * Signal kbase_csf_scheduler_kthread() to allow for the
+ * eventual completion of the current iteration. Once the work is
+ * done, the event_wait wait queue shall be signalled.
+ */
+
+ complete(&kbdev->csf.scheduler.kthread_signal);
+ wait_event(kbdev->csf.event_wait, atomic_read(pending) == 0);
+}
+
void kbase_csf_scheduler_reval_idleness_post_sleep(struct kbase_device *kbdev)
{
u32 csg_nr;