From 6778948f9de86c3cfaf36725a7c87dcff9ba247f Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 08:20:59 +0000 Subject: [PATCH] kernel_5.10 no rt --- kernel/drivers/gpu/arm/bifrost/csf/mali_kbase_csf_csg_debugfs.c | 360 ++++++++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 268 insertions(+), 92 deletions(-) diff --git a/kernel/drivers/gpu/arm/bifrost/csf/mali_kbase_csf_csg_debugfs.c b/kernel/drivers/gpu/arm/bifrost/csf/mali_kbase_csf_csg_debugfs.c index 14deb98..e96044a 100644 --- a/kernel/drivers/gpu/arm/bifrost/csf/mali_kbase_csf_csg_debugfs.c +++ b/kernel/drivers/gpu/arm/bifrost/csf/mali_kbase_csf_csg_debugfs.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note /* * - * (C) COPYRIGHT 2019-2021 ARM Limited. All rights reserved. + * (C) COPYRIGHT 2019-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 @@ -23,10 +23,157 @@ #include <mali_kbase.h> #include <linux/seq_file.h> #include <linux/delay.h> -#include <csf/mali_kbase_csf_trace_buffer.h> +#include <backend/gpu/mali_kbase_pm_internal.h> #if IS_ENABLED(CONFIG_DEBUG_FS) #include "mali_kbase_csf_tl_reader.h" + +/* Wait time to be used cumulatively for all the CSG slots. + * Since scheduler lock is held when STATUS_UPDATE request is sent, there won't be + * any other Host request pending on the FW side and usually FW would be responsive + * to the Doorbell IRQs as it won't do any polling for a long time and also it won't + * have to wait for any HW state transition to complete for publishing the status. + * So it is reasonable to expect that handling of STATUS_UPDATE request would be + * relatively very quick. + */ +#define STATUS_UPDATE_WAIT_TIMEOUT 500 + +/* The bitmask of CSG slots for which the STATUS_UPDATE request completed. + * The access to it is serialized with scheduler lock, so at a time it would + * get used either for "active_groups" or per context "groups" debugfs file. + */ +static DECLARE_BITMAP(csg_slots_status_updated, MAX_SUPPORTED_CSGS); + +static +bool csg_slot_status_update_finish(struct kbase_device *kbdev, u32 csg_nr) +{ + struct kbase_csf_cmd_stream_group_info const *const ginfo = + &kbdev->csf.global_iface.groups[csg_nr]; + + return !((kbase_csf_firmware_csg_input_read(ginfo, CSG_REQ) ^ + kbase_csf_firmware_csg_output(ginfo, CSG_ACK)) & + CSG_REQ_STATUS_UPDATE_MASK); +} + +static +bool csg_slots_status_update_finish(struct kbase_device *kbdev, + const unsigned long *slots_mask) +{ + const u32 max_csg_slots = kbdev->csf.global_iface.group_num; + bool changed = false; + u32 csg_nr; + + lockdep_assert_held(&kbdev->csf.scheduler.lock); + + for_each_set_bit(csg_nr, slots_mask, max_csg_slots) { + if (csg_slot_status_update_finish(kbdev, csg_nr)) { + set_bit(csg_nr, csg_slots_status_updated); + changed = true; + } + } + + return changed; +} + +static void wait_csg_slots_status_update_finish(struct kbase_device *kbdev, + unsigned long *slots_mask) +{ + const u32 max_csg_slots = kbdev->csf.global_iface.group_num; + long remaining = kbase_csf_timeout_in_jiffies(STATUS_UPDATE_WAIT_TIMEOUT); + + lockdep_assert_held(&kbdev->csf.scheduler.lock); + + bitmap_zero(csg_slots_status_updated, max_csg_slots); + + while (!bitmap_empty(slots_mask, max_csg_slots) && remaining) { + remaining = wait_event_timeout(kbdev->csf.event_wait, + csg_slots_status_update_finish(kbdev, slots_mask), + remaining); + if (likely(remaining)) { + bitmap_andnot(slots_mask, slots_mask, + csg_slots_status_updated, max_csg_slots); + } else { + dev_warn(kbdev->dev, + "STATUS_UPDATE request timed out for slots 0x%lx", + slots_mask[0]); + } + } +} + +void kbase_csf_debugfs_update_active_groups_status(struct kbase_device *kbdev) +{ + u32 max_csg_slots = kbdev->csf.global_iface.group_num; + DECLARE_BITMAP(used_csgs, MAX_SUPPORTED_CSGS) = { 0 }; + u32 csg_nr; + unsigned long flags; + + lockdep_assert_held(&kbdev->csf.scheduler.lock); + + /* Global doorbell ring for CSG STATUS_UPDATE request or User doorbell + * ring for Extract offset update, shall not be made when MCU has been + * put to sleep otherwise it will undesirably make MCU exit the sleep + * state. Also it isn't really needed as FW will implicitly update the + * status of all on-slot groups when MCU sleep request is sent to it. + */ + if (kbdev->csf.scheduler.state == SCHED_SLEEPING) { + /* Wait for the MCU sleep request to complete. */ + kbase_pm_wait_for_desired_state(kbdev); + bitmap_copy(csg_slots_status_updated, + kbdev->csf.scheduler.csg_inuse_bitmap, max_csg_slots); + return; + } + + for (csg_nr = 0; csg_nr < max_csg_slots; csg_nr++) { + struct kbase_queue_group *const group = + kbdev->csf.scheduler.csg_slots[csg_nr].resident_group; + if (!group) + continue; + /* Ring the User doorbell for FW to update the Extract offset */ + kbase_csf_ring_doorbell(kbdev, group->doorbell_nr); + set_bit(csg_nr, used_csgs); + } + + /* Return early if there are no on-slot groups */ + if (bitmap_empty(used_csgs, max_csg_slots)) + return; + + kbase_csf_scheduler_spin_lock(kbdev, &flags); + for_each_set_bit(csg_nr, used_csgs, max_csg_slots) { + struct kbase_csf_cmd_stream_group_info const *const ginfo = + &kbdev->csf.global_iface.groups[csg_nr]; + kbase_csf_firmware_csg_input_mask(ginfo, CSG_REQ, + ~kbase_csf_firmware_csg_output(ginfo, CSG_ACK), + CSG_REQ_STATUS_UPDATE_MASK); + } + + BUILD_BUG_ON(MAX_SUPPORTED_CSGS > (sizeof(used_csgs[0]) * BITS_PER_BYTE)); + kbase_csf_ring_csg_slots_doorbell(kbdev, used_csgs[0]); + kbase_csf_scheduler_spin_unlock(kbdev, flags); + wait_csg_slots_status_update_finish(kbdev, used_csgs); + /* Wait for the User doobell ring to take effect */ + msleep(100); +} + +#define MAX_SCHED_STATE_STRING_LEN (16) +static const char *scheduler_state_to_string(struct kbase_device *kbdev, + enum kbase_csf_scheduler_state sched_state) +{ + switch (sched_state) { + case SCHED_BUSY: + return "BUSY"; + case SCHED_INACTIVE: + return "INACTIVE"; + case SCHED_SUSPENDED: + return "SUSPENDED"; +#ifdef KBASE_PM_RUNTIME + case SCHED_SLEEPING: + return "SLEEPING"; +#endif + default: + dev_warn(kbdev->dev, "Unknown Scheduler state %d", sched_state); + return NULL; + } +} /** * blocked_reason_to_string() - Convert blocking reason id to a string @@ -55,16 +202,32 @@ return cs_blocked_reason[reason_id]; } +static bool sb_source_supported(u32 glb_version) +{ + bool supported = false; + + if (((GLB_VERSION_MAJOR_GET(glb_version) == 3) && + (GLB_VERSION_MINOR_GET(glb_version) >= 5)) || + ((GLB_VERSION_MAJOR_GET(glb_version) == 2) && + (GLB_VERSION_MINOR_GET(glb_version) >= 6)) || + ((GLB_VERSION_MAJOR_GET(glb_version) == 1) && + (GLB_VERSION_MINOR_GET(glb_version) >= 3))) + supported = true; + + return supported; +} + static void kbasep_csf_scheduler_dump_active_queue_cs_status_wait( - struct seq_file *file, u32 wait_status, u32 wait_sync_value, - u64 wait_sync_live_value, u64 wait_sync_pointer, u32 sb_status, - u32 blocked_reason) + struct seq_file *file, u32 glb_version, u32 wait_status, u32 wait_sync_value, + u64 wait_sync_live_value, u64 wait_sync_pointer, u32 sb_status, u32 blocked_reason) { #define WAITING "Waiting" #define NOT_WAITING "Not waiting" seq_printf(file, "SB_MASK: %d\n", CS_STATUS_WAIT_SB_MASK_GET(wait_status)); + if (sb_source_supported(glb_version)) + seq_printf(file, "SB_SOURCE: %d\n", CS_STATUS_WAIT_SB_SOURCE_GET(wait_status)); seq_printf(file, "PROGRESS_WAIT: %s\n", CS_STATUS_WAIT_PROGRESS_WAIT_GET(wait_status) ? WAITING : NOT_WAITING); @@ -134,17 +297,16 @@ struct kbase_vmap_struct *mapping; u64 *evt; u64 wait_sync_live_value; + u32 glb_version; if (!queue) return; + glb_version = queue->kctx->kbdev->csf.global_iface.version; + if (WARN_ON(queue->csi_index == KBASEP_IF_NR_INVALID || !queue->group)) return; - - /* Ring the doorbell to have firmware update CS_EXTRACT */ - kbase_csf_ring_cs_user_doorbell(queue->kctx->kbdev, queue); - msleep(100); addr = (u32 *)queue->user_io_addr; cs_insert = addr[CS_INSERT_LO/4] | ((u64)addr[CS_INSERT_HI/4] << 32); @@ -154,16 +316,18 @@ cs_active = addr[CS_ACTIVE/4]; #define KBASEP_CSF_DEBUGFS_CS_HEADER_USER_IO \ - "Bind Idx, Ringbuf addr, Prio, Insert offset, Extract offset, Active, Doorbell\n" + "Bind Idx, Ringbuf addr, Size, Prio, Insert offset, Extract offset, Active, Doorbell\n" - seq_printf(file, KBASEP_CSF_DEBUGFS_CS_HEADER_USER_IO "%8d, %16llx, %4u, %16llx, %16llx, %6u, %8d\n", - queue->csi_index, queue->base_addr, queue->priority, - cs_insert, cs_extract, cs_active, queue->doorbell_nr); + seq_printf(file, KBASEP_CSF_DEBUGFS_CS_HEADER_USER_IO "%8d, %16llx, %8x, %4u, %16llx, %16llx, %6u, %8d\n", + queue->csi_index, queue->base_addr, + queue->size, + queue->priority, cs_insert, cs_extract, cs_active, queue->doorbell_nr); /* Print status information for blocked group waiting for sync object. For on-slot queues, * if cs_trace is enabled, dump the interface's cs_trace configuration. */ if (kbase_csf_scheduler_group_get_slot(queue->group) < 0) { + seq_printf(file, "SAVED_CMD_PTR: 0x%llx\n", queue->saved_cmd_ptr); if (CS_STATUS_WAIT_SYNC_WAIT_GET(queue->status_wait)) { wait_status = queue->status_wait; wait_sync_value = queue->sync_value; @@ -180,9 +344,8 @@ } kbasep_csf_scheduler_dump_active_queue_cs_status_wait( - file, wait_status, wait_sync_value, - wait_sync_live_value, wait_sync_pointer, - sb_status, blocked_reason); + file, glb_version, wait_status, wait_sync_value, + wait_sync_live_value, wait_sync_pointer, sb_status, blocked_reason); } } else { struct kbase_device const *const kbdev = @@ -237,9 +400,8 @@ } kbasep_csf_scheduler_dump_active_queue_cs_status_wait( - file, wait_status, wait_sync_value, - wait_sync_live_value, wait_sync_pointer, sb_status, - blocked_reason); + file, glb_version, wait_status, wait_sync_value, wait_sync_live_value, + wait_sync_pointer, sb_status, blocked_reason); /* Dealing with cs_trace */ if (kbase_csf_scheduler_queue_has_trace(queue)) kbasep_csf_scheduler_dump_active_cs_trace(file, stream); @@ -250,35 +412,18 @@ seq_puts(file, "\n"); } -/* Waiting timeout for STATUS_UPDATE acknowledgment, in milliseconds */ -#define CSF_STATUS_UPDATE_TO_MS (100) - static void kbasep_csf_scheduler_dump_active_group(struct seq_file *file, struct kbase_queue_group *const group) { if (kbase_csf_scheduler_group_get_slot(group) >= 0) { struct kbase_device *const kbdev = group->kctx->kbdev; - unsigned long flags; u32 ep_c, ep_r; char exclusive; + char idle = 'N'; struct kbase_csf_cmd_stream_group_info const *const ginfo = &kbdev->csf.global_iface.groups[group->csg_nr]; - long remaining = - kbase_csf_timeout_in_jiffies(CSF_STATUS_UPDATE_TO_MS); u8 slot_priority = kbdev->csf.scheduler.csg_slots[group->csg_nr].priority; - - kbase_csf_scheduler_spin_lock(kbdev, &flags); - kbase_csf_firmware_csg_input_mask(ginfo, CSG_REQ, - ~kbase_csf_firmware_csg_output(ginfo, CSG_ACK), - CSG_REQ_STATUS_UPDATE_MASK); - kbase_csf_scheduler_spin_unlock(kbdev, flags); - kbase_csf_ring_csg_doorbell(kbdev, group->csg_nr); - - remaining = wait_event_timeout(kbdev->csf.event_wait, - !((kbase_csf_firmware_csg_input_read(ginfo, CSG_REQ) ^ - kbase_csf_firmware_csg_output(ginfo, CSG_ACK)) & - CSG_REQ_STATUS_UPDATE_MASK), remaining); ep_c = kbase_csf_firmware_csg_output(ginfo, CSG_STATUS_EP_CURRENT); @@ -291,18 +436,18 @@ else exclusive = '0'; - if (!remaining) { - dev_err(kbdev->dev, - "Timed out for STATUS_UPDATE on group %d on slot %d", - group->handle, group->csg_nr); + if (kbase_csf_firmware_csg_output(ginfo, CSG_STATUS_STATE) & + CSG_STATUS_STATE_IDLE_MASK) + idle = 'Y'; + if (!test_bit(group->csg_nr, csg_slots_status_updated)) { seq_printf(file, "*** Warn: Timed out for STATUS_UPDATE on slot %d\n", group->csg_nr); - seq_printf(file, "*** The following group-record is likely stale\n"); + seq_puts(file, "*** The following group-record is likely stale\n"); } - seq_puts(file, "GroupID, CSG NR, CSG Prio, Run State, Priority, C_EP(Alloc/Req), F_EP(Alloc/Req), T_EP(Alloc/Req), Exclusive\n"); - seq_printf(file, "%7d, %6d, %8d, %9d, %8d, %11d/%3d, %11d/%3d, %11d/%3d, %9c\n", + seq_puts(file, "GroupID, CSG NR, CSG Prio, Run State, Priority, C_EP(Alloc/Req), F_EP(Alloc/Req), T_EP(Alloc/Req), Exclusive, Idle\n"); + seq_printf(file, "%7d, %6d, %8d, %9d, %8d, %11d/%3d, %11d/%3d, %11d/%3d, %9c, %4c\n", group->handle, group->csg_nr, slot_priority, @@ -314,7 +459,8 @@ CSG_STATUS_EP_REQ_FRAGMENT_EP_GET(ep_r), CSG_STATUS_EP_CURRENT_TILER_EP_GET(ep_c), CSG_STATUS_EP_REQ_TILER_EP_GET(ep_r), - exclusive); + exclusive, + idle); } else { seq_puts(file, "GroupID, CSG NR, Run State, Priority\n"); seq_printf(file, "%7d, %6d, %9d, %8d\n", @@ -352,16 +498,19 @@ { u32 gr; struct kbase_context *const kctx = file->private; - struct kbase_device *const kbdev = kctx->kbdev; + struct kbase_device *kbdev; if (WARN_ON(!kctx)) return -EINVAL; + + kbdev = kctx->kbdev; seq_printf(file, "MALI_CSF_CSG_DEBUGFS_VERSION: v%u\n", MALI_CSF_CSG_DEBUGFS_VERSION); mutex_lock(&kctx->csf.lock); kbase_csf_scheduler_lock(kbdev); + kbase_csf_debugfs_update_active_groups_status(kbdev); for (gr = 0; gr < MAX_QUEUE_GROUP_NUM; gr++) { struct kbase_queue_group *const group = kctx->csf.queue_groups[gr]; @@ -395,6 +544,7 @@ MALI_CSF_CSG_DEBUGFS_VERSION); kbase_csf_scheduler_lock(kbdev); + kbase_csf_debugfs_update_active_groups_status(kbdev); for (csg_nr = 0; csg_nr < num_groups; csg_nr++) { struct kbase_queue_group *const group = kbdev->csf.scheduler.csg_slots[csg_nr].resident_group; @@ -436,11 +586,7 @@ void kbase_csf_queue_group_debugfs_init(struct kbase_context *kctx) { struct dentry *file; -#if (KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE) const mode_t mode = 0444; -#else - const mode_t mode = 0400; -#endif if (WARN_ON(!kctx || IS_ERR_OR_NULL(kctx->kctx_dentry))) return; @@ -492,69 +638,100 @@ return 0; } -DEFINE_SIMPLE_ATTRIBUTE(kbasep_csf_debugfs_scheduling_timer_enabled_fops, - &kbasep_csf_debugfs_scheduling_timer_enabled_get, - &kbasep_csf_debugfs_scheduling_timer_enabled_set, - "%llu\n"); -DEFINE_SIMPLE_ATTRIBUTE(kbasep_csf_debugfs_scheduling_timer_kick_fops, - NULL, - &kbasep_csf_debugfs_scheduling_timer_kick_set, - "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(kbasep_csf_debugfs_scheduling_timer_enabled_fops, + &kbasep_csf_debugfs_scheduling_timer_enabled_get, + &kbasep_csf_debugfs_scheduling_timer_enabled_set, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(kbasep_csf_debugfs_scheduling_timer_kick_fops, NULL, + &kbasep_csf_debugfs_scheduling_timer_kick_set, "%llu\n"); /** - * kbase_csf_debugfs_scheduler_suspend_get() - get if the scheduler is suspended. + * kbase_csf_debugfs_scheduler_state_get() - Get the state of scheduler. * - * @data: The debugfs dentry private data, a pointer to kbase_device - * @val: The debugfs output value, boolean: 1 suspended, 0 otherwise + * @file: Object of the file that is being read. + * @user_buf: User buffer that contains the string. + * @count: Length of user buffer + * @ppos: Offset within file object * - * Return: 0 + * This function will return the current Scheduler state to Userspace + * Scheduler may exit that state by the time the state string is received + * by the Userspace. + * + * Return: 0 if Scheduler was found in an unexpected state, or the + * size of the state string if it was copied successfully to the + * User buffer or a negative value in case of an error. */ -static int kbase_csf_debugfs_scheduler_suspend_get( - void *data, u64 *val) +static ssize_t kbase_csf_debugfs_scheduler_state_get(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) { - struct kbase_device *kbdev = data; + struct kbase_device *kbdev = file->private_data; struct kbase_csf_scheduler *scheduler = &kbdev->csf.scheduler; + const char *state_string; kbase_csf_scheduler_lock(kbdev); - *val = (scheduler->state == SCHED_SUSPENDED); + state_string = scheduler_state_to_string(kbdev, scheduler->state); kbase_csf_scheduler_unlock(kbdev); - return 0; + if (!state_string) + count = 0; + + return simple_read_from_buffer(user_buf, count, ppos, + state_string, strlen(state_string)); } /** - * kbase_csf_debugfs_scheduler_suspend_set() - set the scheduler to suspended. + * kbase_csf_debugfs_scheduler_state_set() - Set the state of scheduler. * - * @data: The debugfs dentry private data, a pointer to kbase_device - * @val: The debugfs input value, boolean: 1 suspend, 0 otherwise + * @file: Object of the file that is being written to. + * @ubuf: User buffer that contains the string. + * @count: Length of user buffer + * @ppos: Offset within file object * - * Return: Negative value if already in requested state, 0 otherwise. + * This function will update the Scheduler state as per the state string + * passed by the Userspace. Scheduler may or may not remain in new state + * for long. + * + * Return: Negative value if the string doesn't correspond to a valid Scheduler + * state or if copy from user buffer failed, otherwise the length of + * the User buffer. */ -static int kbase_csf_debugfs_scheduler_suspend_set( - void *data, u64 val) +static ssize_t kbase_csf_debugfs_scheduler_state_set(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) { - struct kbase_device *kbdev = data; - struct kbase_csf_scheduler *scheduler = &kbdev->csf.scheduler; - enum kbase_csf_scheduler_state state; + struct kbase_device *kbdev = file->private_data; + char buf[MAX_SCHED_STATE_STRING_LEN]; + ssize_t ret = count; - kbase_csf_scheduler_lock(kbdev); - state = scheduler->state; - kbase_csf_scheduler_unlock(kbdev); + CSTD_UNUSED(ppos); - if (val && (state != SCHED_SUSPENDED)) + count = min_t(size_t, sizeof(buf) - 1, count); + if (copy_from_user(buf, ubuf, count)) + return -EFAULT; + + buf[count] = 0; + + if (sysfs_streq(buf, "SUSPENDED")) kbase_csf_scheduler_pm_suspend(kbdev); - else if (!val && (state == SCHED_SUSPENDED)) - kbase_csf_scheduler_pm_resume(kbdev); - else - return -1; +#ifdef KBASE_PM_RUNTIME + else if (sysfs_streq(buf, "SLEEPING")) + kbase_csf_scheduler_force_sleep(kbdev); +#endif + else if (sysfs_streq(buf, "INACTIVE")) + kbase_csf_scheduler_force_wakeup(kbdev); + else { + dev_dbg(kbdev->dev, "Bad scheduler state %s", buf); + ret = -EINVAL; + } - return 0; + return ret; } -DEFINE_SIMPLE_ATTRIBUTE(kbasep_csf_debugfs_scheduler_suspend_fops, - &kbase_csf_debugfs_scheduler_suspend_get, - &kbase_csf_debugfs_scheduler_suspend_set, - "%llu\n"); +static const struct file_operations kbasep_csf_debugfs_scheduler_state_fops = { + .owner = THIS_MODULE, + .read = kbase_csf_debugfs_scheduler_state_get, + .write = kbase_csf_debugfs_scheduler_state_set, + .open = simple_open, + .llseek = default_llseek, +}; void kbase_csf_debugfs_init(struct kbase_device *kbdev) { @@ -568,12 +745,11 @@ debugfs_create_file("scheduling_timer_kick", 0200, kbdev->mali_debugfs_directory, kbdev, &kbasep_csf_debugfs_scheduling_timer_kick_fops); - debugfs_create_file("scheduler_suspend", 0644, + debugfs_create_file("scheduler_state", 0644, kbdev->mali_debugfs_directory, kbdev, - &kbasep_csf_debugfs_scheduler_suspend_fops); + &kbasep_csf_debugfs_scheduler_state_fops); kbase_csf_tl_reader_debugfs_init(kbdev); - kbase_csf_firmware_trace_buffer_debugfs_init(kbdev); } #else -- Gitblit v1.6.2