From 0f04d6c2046f5c87b52687380258a8d95dd46c65 Mon Sep 17 00:00:00 2001 From: amitabhd Date: Mon, 16 Dec 2024 06:10:23 +0000 Subject: [PATCH] host1x_emu: HR Timer Syncpoint Fence Polling 1. This change enable HR Timer Syncpoint polling and disable the thread based fence polling. 2. Default timer interval is 200usec. The timer value is taken from DT. If the timer interval in DT is less then 50usec, default timer interval is taken. Jira HOSTX-5527 Change-Id: I6644f1362f28a8901e4e384f1290be9807c30036 Signed-off-by: amitabhd Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3268636 GVS: buildbot_gerritrpt Tested-by: mobile promotions Reviewed-by: Raghavendra Vishnu Kumar Reviewed-by: mobile promotions Reviewed-by: Santosh BS --- drivers/gpu/host1x-emu/Makefile | 3 + drivers/gpu/host1x-emu/dev.c | 19 +++- drivers/gpu/host1x-emu/dev.h | 9 +- drivers/gpu/host1x-emu/poll.c | 181 +++++++++++++++++++++----------- drivers/gpu/host1x-emu/poll.h | 5 + 5 files changed, 146 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/host1x-emu/Makefile b/drivers/gpu/host1x-emu/Makefile index 351df0db..1f02358e 100644 --- a/drivers/gpu/host1x-emu/Makefile +++ b/drivers/gpu/host1x-emu/Makefile @@ -9,6 +9,9 @@ endif #Enable Tasklet based fence scanning in SyncpointIncr #ccflags-y += -DHOST1X_EMU_SYNC_INC_TASKLET +# Enable HRTimer based fence scanning +ccflags-y += -DHOST1X_EMU_HRTIMER_FENCE_SCAN + ifeq ($(NV_BUILD_CONFIGURATION_EXPOSING_T26X), 1) LINUXINCLUDE += -I$(srctree.nvidia-oot)/drivers/gpu/host1x-emu/include # Enable for verification in VDK. Below allow Emulated Syncpoint driver diff --git a/drivers/gpu/host1x-emu/dev.c b/drivers/gpu/host1x-emu/dev.c index 053a2a82..686da4aa 100644 --- a/drivers/gpu/host1x-emu/dev.c +++ b/drivers/gpu/host1x-emu/dev.c @@ -1,7 +1,5 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: GPL-2.0-only - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. #include #include #include @@ -93,6 +91,19 @@ static int host1x_get_assigned_resources(struct host1x *host) host->polling_intrval = HOST1X_POOL_MSEC_PERIOD; } +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + err = of_property_read_u32_array(np, "nvidia,hr-polling-interval", vals, 1); + if (err == 0) { + host->hr_polling_intrval = vals[0]; + if (host->hr_polling_intrval < 50) + host->hr_polling_intrval = HRTIMER_TIMEOUT_NSEC; + } else { + host->hr_polling_intrval = HRTIMER_TIMEOUT_NSEC; + } + pr_info("Host1x-EMU: HRTimer Resolution :%unsec\n", MONOTONIC_RES_NSEC); + pr_info("Host1x-EMU: HRTimer Polling Interval :%unsec\n", host->hr_polling_intrval); +#endif + #ifdef HOST1X_EMU_HYPERVISOR err = of_property_read_u32_array(np, "nvidia,syncpoints-mem", vals, 4); if (err == 0) { diff --git a/drivers/gpu/host1x-emu/dev.h b/drivers/gpu/host1x-emu/dev.h index 6b702203..f6ca638c 100644 --- a/drivers/gpu/host1x-emu/dev.h +++ b/drivers/gpu/host1x-emu/dev.h @@ -1,7 +1,5 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: GPL-2.0-only - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. #ifndef HOST1X_DEV_H #define HOST1X_DEV_H @@ -59,6 +57,9 @@ struct host1x { unsigned int syncpt_base; unsigned int syncpt_count; unsigned int polling_intrval; +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + unsigned int hr_polling_intrval; +#endif #ifdef HOST1X_EMU_HYPERVISOR void __iomem *syncpt_va_apt; /* syncpoint apperture mapped in kernel space */ #else diff --git a/drivers/gpu/host1x-emu/poll.c b/drivers/gpu/host1x-emu/poll.c index 13519cf5..59023cd1 100644 --- a/drivers/gpu/host1x-emu/poll.c +++ b/drivers/gpu/host1x-emu/poll.c @@ -4,46 +4,88 @@ #include "fence.h" #include "poll.h" -static void host1x_poll_add_fence_to_list(struct host1x_fence_list *list, - struct host1x_syncpt_fence *fence) +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + +struct host1x *hr_timer_host; +static struct hrtimer emu_hr_timer; + +//Timer Callback function. This will be called when timer expires +static enum hrtimer_restart timer_callback(struct hrtimer *timer) { - struct host1x_syncpt_fence *fence_in_list; + int id; + unsigned long irqflags; + struct host1x_syncpt *sp; + struct host1x_syncpt *tmp_spt; + struct host1x *host = hr_timer_host; + ktime_t ts = ktime_get(); - list_for_each_entry_reverse(fence_in_list, &list->list, list) { - if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) { - /* Fence in list is before us, we can insert here */ - list_add(&fence->list, &fence_in_list->list); - return; - } - } + for (id = 0; id < host->num_pools + 1; ++id) { + struct host1x_syncpt_pool *pool = &host->pools[id]; - /* Add as first in list */ - list_add(&fence->list, &list->list); + list_for_each_entry_safe(sp, tmp_spt, &pool->syncpt_list.list, list) { + struct host1x_syncpt_fence *fence, *tmp; + unsigned int value; + + value = host1x_syncpt_load(sp); + + spin_lock_irqsave(&sp->fences.lock, irqflags); + list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) { + if (((value - fence->threshold) & 0x80000000U) != 0U) { + /* Fence is not yet expired, we are done */ + break; + } + + list_del_init(&fence->list); + host1x_fence_signal(fence, ts); + } + spin_unlock_irqrestore(&sp->fences.lock, irqflags); + } + } + hrtimer_forward_now(timer, ktime_set(HRTIMER_TIMEOUT_SEC, host->hr_polling_intrval)); + return HRTIMER_RESTART; +} +#endif + +static void host1x_poll_add_fence_to_list(struct host1x_fence_list *list, + struct host1x_syncpt_fence *fence) +{ + struct host1x_syncpt_fence *fence_in_list; + + list_for_each_entry_reverse(fence_in_list, &list->list, list) { + if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) { + /* Fence in list is before us, we can insert here */ + list_add(&fence->list, &fence_in_list->list); + return; + } + } + + /* Add as first in list */ + list_add(&fence->list, &list->list); } void host1x_poll_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence) { - struct host1x_fence_list *fence_list = &fence->sp->fences; + struct host1x_fence_list *fence_list = &fence->sp->fences; - INIT_LIST_HEAD(&fence->list); - host1x_poll_add_fence_to_list(fence_list, fence); + INIT_LIST_HEAD(&fence->list); + host1x_poll_add_fence_to_list(fence_list, fence); } bool host1x_poll_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence) { - struct host1x_fence_list *fence_list = &fence->sp->fences; - unsigned long irqflags; + struct host1x_fence_list *fence_list = &fence->sp->fences; + unsigned long irqflags; - spin_lock_irqsave(&fence_list->lock, irqflags); + spin_lock_irqsave(&fence_list->lock, irqflags); - if (list_empty(&fence->list)) { - spin_unlock_irqrestore(&fence_list->lock, irqflags); - return false; - } - list_del_init(&fence->list); + if (list_empty(&fence->list)) { + spin_unlock_irqrestore(&fence_list->lock, irqflags); + return false; + } + list_del_init(&fence->list); - spin_unlock_irqrestore(&fence_list->lock, irqflags); - return true; + spin_unlock_irqrestore(&fence_list->lock, irqflags); + return true; } static void host1x_pool_timeout_handler(struct work_struct *work) @@ -68,38 +110,38 @@ static void host1x_pool_timeout_handler(struct work_struct *work) int host1x_poll_init(struct host1x *host) { - unsigned int id; + unsigned int id; - for (id = 0; id < host->num_pools; ++id) { - struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; + for (id = 0; id < host->num_pools; ++id) { + struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; - syncpt_pool->host = host; - spin_lock_init(&syncpt_pool->syncpt_list.lock); - INIT_LIST_HEAD(&syncpt_pool->syncpt_list.list); + syncpt_pool->host = host; + spin_lock_init(&syncpt_pool->syncpt_list.lock); + INIT_LIST_HEAD(&syncpt_pool->syncpt_list.list); - INIT_DELAYED_WORK(&syncpt_pool->pool_work, host1x_pool_timeout_handler); - } + INIT_DELAYED_WORK(&syncpt_pool->pool_work, host1x_pool_timeout_handler); + } - /* Initialize RO-Pool*/ - host->pools[host->ro_pool_id].host = host; - spin_lock_init(&host->pools[host->ro_pool_id].syncpt_list.lock); - INIT_LIST_HEAD(&host->pools[host->ro_pool_id].syncpt_list.list); - INIT_DELAYED_WORK(&host->pools[host->ro_pool_id].pool_work, - host1x_pool_timeout_handler); + /* Initialize RO-Pool*/ + host->pools[host->ro_pool_id].host = host; + spin_lock_init(&host->pools[host->ro_pool_id].syncpt_list.lock); + INIT_LIST_HEAD(&host->pools[host->ro_pool_id].syncpt_list.list); + INIT_DELAYED_WORK(&host->pools[host->ro_pool_id].pool_work, + host1x_pool_timeout_handler); - for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) { - struct host1x_syncpt *syncpt = &host->syncpt[id]; - struct host1x_syncpt_pool *pool = syncpt->pool; + for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) { + struct host1x_syncpt *syncpt = &host->syncpt[id]; + struct host1x_syncpt_pool *pool = syncpt->pool; - spin_lock_init(&syncpt->fences.lock); - INIT_LIST_HEAD(&syncpt->fences.list); - INIT_LIST_HEAD(&syncpt->list); + spin_lock_init(&syncpt->fences.lock); + INIT_LIST_HEAD(&syncpt->fences.list); + INIT_LIST_HEAD(&syncpt->list); /* Add syncpoint to pool list*/ list_add_tail(&syncpt->list, &pool->syncpt_list.list); } - return 0; + return 0; } void host1x_poll_irq_check_syncpt_fence(struct host1x_syncpt *sp) @@ -125,29 +167,42 @@ void host1x_poll_irq_check_syncpt_fence(struct host1x_syncpt *sp) void host1x_poll_start(struct host1x *host) { - int id; + int id; +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + ktime_t ktime; - /*Loop till "host->num_pools + 1" to include Ro-Pool*/ - for (id = 0; id < host->num_pools + 1; ++id) { - struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; + hr_timer_host = host; + ktime = ktime_set(HRTIMER_TIMEOUT_SEC, host->hr_polling_intrval); + hrtimer_init(&emu_hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + emu_hr_timer.function = &timer_callback; + hrtimer_start(&emu_hr_timer, ktime, HRTIMER_MODE_REL); +#endif - schedule_delayed_work(&syncpt_pool->pool_work, msecs_to_jiffies(host->polling_intrval)); - } + /*Loop till "host->num_pools + 1" to include Ro-Pool*/ + for (id = 0; id < host->num_pools + 1; ++id) { + struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; + + schedule_delayed_work(&syncpt_pool->pool_work, msecs_to_jiffies(host->polling_intrval)); + } } void host1x_poll_stop(struct host1x *host) { - int id; + int id; - /*Loop till "host->num_pools + 1" to include Ro-Pool*/ - for (id = 0; id < host->num_pools + 1; ++id) { - struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + hrtimer_cancel(&emu_hr_timer); +#endif - //Schedule delayed work immediately - schedule_delayed_work(&syncpt_pool->pool_work, 0); - //Wait for schedule work to complete - flush_delayed_work(&syncpt_pool->pool_work); - //Cancel the work as it reschedule itself - cancel_delayed_work(&syncpt_pool->pool_work); - } + /*Loop till "host->num_pools + 1" to include Ro-Pool*/ + for (id = 0; id < host->num_pools + 1; ++id) { + struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; + + //Schedule delayed work immediately + schedule_delayed_work(&syncpt_pool->pool_work, 0); + //Wait for schedule work to complete + flush_delayed_work(&syncpt_pool->pool_work); + //Cancel the work as it reschedule itself + cancel_delayed_work(&syncpt_pool->pool_work); + } } diff --git a/drivers/gpu/host1x-emu/poll.h b/drivers/gpu/host1x-emu/poll.h index dfa1c1bd..d1af8a85 100644 --- a/drivers/gpu/host1x-emu/poll.h +++ b/drivers/gpu/host1x-emu/poll.h @@ -7,6 +7,11 @@ #include #include +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN +#define HRTIMER_TIMEOUT_NSEC 200000U /*200usec*/ +#define HRTIMER_TIMEOUT_SEC 0U /*0sec*/ +#endif /*HOST1X_EMU_HRTIMER_FENCE_SCAN*/ + struct host1x; struct host1x_syncpt; struct host1x_syncpt_fence;