diff --git a/arch/nvgpu-interface.yaml b/arch/nvgpu-interface.yaml index 17e06092f..f795ede66 100644 --- a/arch/nvgpu-interface.yaml +++ b/arch/nvgpu-interface.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2019-2021, NVIDIA CORPORATION. All Rights Reserved. +# Copyright (c) 2019-2022, NVIDIA CORPORATION. All Rights Reserved. # # OS interface units and utilities. Often represented by simply a header file. # @@ -109,7 +109,8 @@ thread: timers: safe: yes - sources: [ include/nvgpu/timers.h ] + sources: [ include/nvgpu/timers.h, + include/nvgpu/periodic_timer.h ] types: safe: yes diff --git a/arch/nvgpu-linux.yaml b/arch/nvgpu-linux.yaml index 899efe9a6..2087ac487 100644 --- a/arch/nvgpu-linux.yaml +++ b/arch/nvgpu-linux.yaml @@ -220,7 +220,8 @@ thread: sources: [ os/linux/thread.c ] timers: - sources: [ os/linux/timers.c ] + sources: [ os/linux/timers.c, + os/linux/periodic_timer.c ] vgpu: sources: [ os/linux/vgpu/fecs_trace_vgpu_linux.c, @@ -254,6 +255,7 @@ headers: include/nvgpu/linux/nvgpu_mem.h, include/nvgpu/linux/os_fence_android.h, include/nvgpu/linux/os_fence_dma.h, + include/nvgpu/linux/periodic_timer.h, include/nvgpu/linux/rwsem.h, include/nvgpu/linux/nvmem.h, include/nvgpu/linux/sim.h, diff --git a/arch/nvgpu-posix.yaml b/arch/nvgpu-posix.yaml index 588b0397f..935f44148 100644 --- a/arch/nvgpu-posix.yaml +++ b/arch/nvgpu-posix.yaml @@ -114,7 +114,9 @@ timers: safe: yes owner: Ajesh K sources: [ os/posix/timers.c, - include/nvgpu/posix/timers.h ] + os/posix/periodic_timer.c, + include/nvgpu/posix/timers.h, + include/nvgpu/posix/periodic_timer.h ] deps: atomic: diff --git a/drivers/gpu/nvgpu/Makefile b/drivers/gpu/nvgpu/Makefile index 3310f0b80..5e3934f94 100644 --- a/drivers/gpu/nvgpu/Makefile +++ b/drivers/gpu/nvgpu/Makefile @@ -483,7 +483,8 @@ nvgpu-y += \ os/linux/bsearch.o \ os/linux/cic/cic_report_err.o \ os/linux/dmabuf_priv.o \ - os/linux/power_ops.o + os/linux/power_ops.o \ + os/linux/periodic_timer.o nvgpu-$(CONFIG_NVGPU_IVM_BUILD) += \ os/linux/nvgpu_ivm.o \ diff --git a/drivers/gpu/nvgpu/Makefile.sources b/drivers/gpu/nvgpu/Makefile.sources index ec4520d30..54dd87480 100644 --- a/drivers/gpu/nvgpu/Makefile.sources +++ b/drivers/gpu/nvgpu/Makefile.sources @@ -78,6 +78,7 @@ endif srcs += os/posix/bug.c \ os/posix/rwsem.c \ os/posix/timers.c \ + os/posix/periodic_timer.c \ os/posix/cond.c \ os/posix/lock.c \ os/posix/thread.c \ diff --git a/drivers/gpu/nvgpu/common/gr/fecs_trace.c b/drivers/gpu/nvgpu/common/gr/fecs_trace.c index 241a3cc2c..7dc48ef63 100644 --- a/drivers/gpu/nvgpu/common/gr/fecs_trace.c +++ b/drivers/gpu/nvgpu/common/gr/fecs_trace.c @@ -35,7 +35,7 @@ #include #include -static int nvgpu_gr_fecs_trace_periodic_polling(void *arg); +static void nvgpu_gr_fecs_trace_periodic_polling(void *arg); int nvgpu_gr_fecs_trace_add_context(struct gk20a *g, u32 context_ptr, pid_t pid, u32 vmid, struct nvgpu_list_node *list) @@ -134,6 +134,7 @@ void nvgpu_gr_fecs_trace_find_pid(struct gk20a *g, u32 context_ptr, int nvgpu_gr_fecs_trace_init(struct gk20a *g) { struct nvgpu_gr_fecs_trace *trace; + int err; if (!is_power_of_2((u32)GK20A_FECS_TRACE_NUM_RECORDS)) { nvgpu_err(g, "invalid NUM_RECORDS chosen"); @@ -157,7 +158,13 @@ int nvgpu_gr_fecs_trace_init(struct gk20a *g) trace->enable_count = 0; - return 0; + err = nvgpu_periodic_timer_init(&trace->poll_timer, + nvgpu_gr_fecs_trace_periodic_polling, g); + if (err != 0) { + nvgpu_err(g, "failed to create fecs_trace timer err=%d", err); + } + + return err; } int nvgpu_gr_fecs_trace_deinit(struct gk20a *g) @@ -170,11 +177,12 @@ int nvgpu_gr_fecs_trace_deinit(struct gk20a *g) /* * Check if tracer was enabled before attempting to stop the - * tracer thread. + * tracer timer. */ if (trace->enable_count > 0) { - nvgpu_thread_stop(&trace->poll_task); + nvgpu_periodic_timer_stop(&trace->poll_timer); } + nvgpu_periodic_timer_destroy(&trace->poll_timer); nvgpu_gr_fecs_trace_remove_contexts(g, &trace->context_list); @@ -280,10 +288,10 @@ int nvgpu_gr_fecs_trace_enable(struct gk20a *g) g->ops.gr.fecs_trace.set_read_index(g, write); } - err = nvgpu_thread_create(&trace->poll_task, g, - nvgpu_gr_fecs_trace_periodic_polling, __func__); + err = nvgpu_periodic_timer_start(&trace->poll_timer, + GK20A_FECS_TRACE_FRAME_PERIOD_NS); if (err != 0) { - nvgpu_warn(g, "failed to create FECS polling task"); + nvgpu_warn(g, "failed to start FECS polling timer"); goto done; } } @@ -333,7 +341,7 @@ int nvgpu_gr_fecs_trace_disable(struct gk20a *g) g->ops.gr.fecs_trace.set_read_index(g, read); } } - nvgpu_thread_stop(&trace->poll_task); + nvgpu_periodic_timer_stop(&trace->poll_timer); } nvgpu_mutex_release(&trace->enable_lock); @@ -572,23 +580,14 @@ done_unlock: return err; } -static int nvgpu_gr_fecs_trace_periodic_polling(void *arg) +static void nvgpu_gr_fecs_trace_periodic_polling(void *arg) { struct gk20a *g = (struct gk20a *)arg; struct nvgpu_gr_fecs_trace *trace = g->fecs_trace; - nvgpu_log(g, gpu_dbg_ctxsw, "thread running"); - - while (!nvgpu_thread_should_stop(&trace->poll_task) && - trace->enable_count > 0U) { - - nvgpu_usleep_range(GK20A_FECS_TRACE_FRAME_PERIOD_US, - GK20A_FECS_TRACE_FRAME_PERIOD_US * 2U); - + if (trace->enable_count > 0U) { nvgpu_gr_fecs_trace_poll(g); } - - return 0; } int nvgpu_gr_fecs_trace_reset(struct gk20a *g) diff --git a/drivers/gpu/nvgpu/include/nvgpu/gr/fecs_trace.h b/drivers/gpu/nvgpu/include/nvgpu/gr/fecs_trace.h index 83df2799b..2efdab06f 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/gr/fecs_trace.h +++ b/drivers/gpu/nvgpu/include/nvgpu/gr/fecs_trace.h @@ -28,14 +28,14 @@ #include #include #include -#include +#include /* * If HW circular buffer is getting too many "buffer full" conditions, * increasing this constant should help (it drives Linux' internal buffer size). */ #define GK20A_FECS_TRACE_NUM_RECORDS (1 << 10) -#define GK20A_FECS_TRACE_FRAME_PERIOD_US (1000000ULL/60ULL) +#define GK20A_FECS_TRACE_FRAME_PERIOD_NS (1000000000ULL/60ULL) #define GK20A_FECS_TRACE_PTIMER_SHIFT 5 #define NVGPU_GPU_CTXSW_TAG_SOF 0x00U @@ -71,7 +71,7 @@ struct nvgpu_gr_fecs_trace { struct nvgpu_mutex list_lock; struct nvgpu_mutex poll_lock; - struct nvgpu_thread poll_task; + struct nvgpu_periodic_timer poll_timer; struct nvgpu_mutex enable_lock; u32 enable_count; diff --git a/drivers/gpu/nvgpu/include/nvgpu/linux/periodic_timer.h b/drivers/gpu/nvgpu/include/nvgpu/linux/periodic_timer.h new file mode 100644 index 000000000..184c64b56 --- /dev/null +++ b/drivers/gpu/nvgpu/include/nvgpu/linux/periodic_timer.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef NVGPU_LINUX_PERIODIC_TIMER_H +#define NVGPU_LINUX_PERIODIC_TIMER_H + +#include +#include + +struct nvgpu_periodic_timer { + struct hrtimer timer; + void (*fn)(void *arg); + void *arg; + ktime_t interval; + async_cookie_t async_cookie; + bool enabled; + raw_spinlock_t lock; + +}; + +#endif \ No newline at end of file diff --git a/drivers/gpu/nvgpu/include/nvgpu/periodic_timer.h b/drivers/gpu/nvgpu/include/nvgpu/periodic_timer.h new file mode 100644 index 000000000..83fd8d975 --- /dev/null +++ b/drivers/gpu/nvgpu/include/nvgpu/periodic_timer.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef NVGPU_PERIODIC_TIMER_H +#define NVGPU_PERIODIC_TIMER_H + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#include + +/** + * @brief Initialize a nvgpu_periodic_timer + * + * @param timer The timer to be initialized + * @param fn Timer callback function, must not be NULL + * @param arg The argument of the timer callback function + * @return 0 Success + * @return -EAGAIN Temporary error during kernel allocation of timer structure + * @return -EINVAL OS specific implementation error + * @return -ENOMEM out of memory + */ +int nvgpu_periodic_timer_init(struct nvgpu_periodic_timer *timer, + void (*fn)(void *arg), void *arg); +/** + * @brief start a nvgpu_periodic_timer. Not thread safe. + * + * @param timer timer to start, must not be NULL + * @param interval_ns periodic timer interval in the unit of ns + * @return 0 Success + * @return -EINVAL invalid timer or interval_ns + */ +int nvgpu_periodic_timer_start(struct nvgpu_periodic_timer *timer, + u64 interval_ns); +/** + * @brief stop a timer and wait for any timer callback to be finished. + * Not thread safe. + * + * @param timer timer to stop + * @return 0 Success + * @return -EINVAL invalid timer + */ +int nvgpu_periodic_timer_stop(struct nvgpu_periodic_timer *timer); +/** + * @brief destroy a nvgpu_periodic_timer + * + * @param timer timer to destroy + * @return 0 Success + * @return -EINVAL invalid timer + */ +int nvgpu_periodic_timer_destroy(struct nvgpu_periodic_timer *timer); + +#endif diff --git a/drivers/gpu/nvgpu/include/nvgpu/posix/periodic_timer.h b/drivers/gpu/nvgpu/include/nvgpu/posix/periodic_timer.h new file mode 100644 index 000000000..1db3817d3 --- /dev/null +++ b/drivers/gpu/nvgpu/include/nvgpu/posix/periodic_timer.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef NVGPU_POSIX_PERIODIC_TIMER_H +#define NVGPU_POSIX_PERIODIC_TIMER_H + +#include +#include +#include + +struct nvgpu_periodic_timer { + timer_t timerid; + void (*fn)(void *arg); + void *arg; + struct itimerspec ts; + bool enabled; + bool last_run_done; + struct nvgpu_cond cond; +}; + +#endif diff --git a/drivers/gpu/nvgpu/os/linux/periodic_timer.c b/drivers/gpu/nvgpu/os/linux/periodic_timer.c new file mode 100644 index 000000000..8bcb8421a --- /dev/null +++ b/drivers/gpu/nvgpu/os/linux/periodic_timer.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +static void async_func(void *data, async_cookie_t cookie) +{ + struct nvgpu_periodic_timer *timer = data; + + timer->fn(timer->arg); + raw_spin_lock(&timer->lock); + if (timer->enabled) { + hrtimer_start(&timer->timer, timer->interval, HRTIMER_MODE_REL); + } + raw_spin_unlock(&timer->lock); +} + +static enum hrtimer_restart timer_callback(struct hrtimer *os_timer) +{ + struct nvgpu_periodic_timer *timer = + container_of(os_timer, struct nvgpu_periodic_timer, timer); + + timer->async_cookie = async_schedule(async_func, timer); + return HRTIMER_NORESTART; +} + +int nvgpu_periodic_timer_init(struct nvgpu_periodic_timer *timer, + void (*fn)(void *arg), void *arg) +{ + hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + timer->timer.function = timer_callback; + timer->fn = fn; + timer->arg = arg; + timer->interval = ktime_set(0, 0); + timer->async_cookie = 0; + timer->enabled = false; + raw_spin_lock_init(&timer->lock); + return 0; +} + +int nvgpu_periodic_timer_start(struct nvgpu_periodic_timer *timer, + u64 interval_ns) +{ + raw_spin_lock(&timer->lock); + timer->interval = ns_to_ktime(interval_ns); + timer->enabled = true; + timer->async_cookie = 0; + hrtimer_start(&timer->timer, timer->interval, HRTIMER_MODE_REL); + raw_spin_unlock(&timer->lock); + + return 0; +} + +int nvgpu_periodic_timer_stop(struct nvgpu_periodic_timer *timer) +{ + raw_spin_lock(&timer->lock); + if (!timer->enabled) { + raw_spin_unlock(&timer->lock); + return 0; + } + timer->enabled = false; + raw_spin_unlock(&timer->lock); + hrtimer_cancel(&timer->timer); + if (timer->async_cookie != 0) { + async_synchronize_cookie(timer->async_cookie + 1); + } + return 0; +} + +int nvgpu_periodic_timer_destroy(struct nvgpu_periodic_timer *timer) +{ + return nvgpu_periodic_timer_stop(timer); +} diff --git a/drivers/gpu/nvgpu/os/posix/periodic_timer.c b/drivers/gpu/nvgpu/os/posix/periodic_timer.c new file mode 100644 index 000000000..486a5b9c6 --- /dev/null +++ b/drivers/gpu/nvgpu/os/posix/periodic_timer.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +static void timer_callback(union sigval arg) +{ + struct nvgpu_periodic_timer *timer = arg.sival_ptr; + + timer->fn(timer->arg); + nvgpu_cond_lock(&timer->cond); + if (!timer->enabled) { + timer->last_run_done = true; + nvgpu_cond_broadcast_locked(&timer->cond); + } else { + int err; + err = timer_settime(timer->timerid, 0, &timer->ts, NULL); + nvgpu_assert(err == 0); + } + nvgpu_cond_unlock(&timer->cond); +} + +int nvgpu_periodic_timer_init(struct nvgpu_periodic_timer *timer, + void (*fn)(void *arg), void *arg) +{ + struct sigevent se = {}; + int err; + + se.sigev_notify = SIGEV_THREAD; + se.sigev_notify_function = timer_callback; + se.sigev_value.sival_ptr = timer; + + err = timer_create(CLOCK_MONOTONIC, &se, &timer->timerid); + if (err == -1) { + err = -errno; + } else { + timer->fn = fn; + timer->arg = arg; + timer->enabled = false; + timer->last_run_done = false; + nvgpu_cond_init(&timer->cond); + } + return err; +} + +#define S2NS 1000000000UL +int nvgpu_periodic_timer_start(struct nvgpu_periodic_timer *timer, + u64 interval_ns) +{ + struct itimerspec *ts = &timer->ts; + int err; + + memset(ts, 0, sizeof(*ts)); + ts->it_value.tv_sec = (time_t)(interval_ns / S2NS); + ts->it_value.tv_nsec = (long)(interval_ns % S2NS); + + timer->enabled = true; + err = timer_settime(timer->timerid, 0, ts, NULL); + if (err == -1) { + err = -errno; + } + return err; +} + +int nvgpu_periodic_timer_stop(struct nvgpu_periodic_timer *timer) +{ + struct itimerspec *ts = &timer->ts; + struct itimerspec old_ts = {}; + int err; + + nvgpu_cond_lock(&timer->cond); + if (!timer->enabled) { + nvgpu_cond_unlock(&timer->cond); + return 0; + } + timer->enabled = false; + timer->last_run_done = false; + nvgpu_cond_unlock(&timer->cond); + + /* no one will restart the single shot timer from now */ + + ts->it_value.tv_sec = 0; + ts->it_value.tv_nsec = 0; + err = timer_settime(timer->timerid, 0, ts, &old_ts); + if (err == -1) { + err = -errno; + return err; + } + + if (old_ts.it_value.tv_sec == 0 && old_ts.it_value.tv_nsec == 0) { + /* timer is running or prepared to run */ + err = NVGPU_COND_WAIT(&timer->cond, + timer->last_run_done, 0U); + } + return err; +} + +int nvgpu_periodic_timer_destroy(struct nvgpu_periodic_timer *timer) +{ + int err; + + err = nvgpu_periodic_timer_stop(timer); + if (err != 0) { + return err; + } + err = timer_delete(timer->timerid); + if (err == 0) { + nvgpu_cond_destroy(&timer->cond); + } else { + err = -errno; + } + return err; +} diff --git a/libs/Makefile.common.tmk b/libs/Makefile.common.tmk index 25c0488cb..73ed5be0c 100644 --- a/libs/Makefile.common.tmk +++ b/libs/Makefile.common.tmk @@ -48,7 +48,7 @@ NV_COMPONENT_INCLUDES := \ $(NVGPU_NEXT_SOURCE)/include \ $(NVGPU_NEXT_SOURCE)/../../../include ifneq ($(NV_BUILD_CONFIGURATION_OS_IS_QNX),1) -NV_COMPONENT_SYSTEM_SHARED_LIBRARIES += pthread +NV_COMPONENT_SYSTEM_SHARED_LIBRARIES += pthread rt NVGPU_FORCE_SAFETY_PROFILE := 1 NVGPU_FORCE_DEBUG_PROFILE := 1 endif