gpu: nvgpu: add periodic timer API

move fecs_trace polling from kthread to timer API.

Jira GVSCI-10883

Signed-off-by: Richard Zhao <rizhao@nvidia.com>
Change-Id: I224754b7205f1d0eefdc19a73a98f42e4d3e9d0e
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2700601
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: svc-mobile-cert <svc-mobile-cert@nvidia.com>
Reviewed-by: Shashank Singh <shashsingh@nvidia.com>
Reviewed-by: Aparna Das <aparnad@nvidia.com>
GVS: Gerrit_Virtual_Submit
This commit is contained in:
Richard Zhao
2022-04-20 11:05:43 -07:00
committed by mobile promotions
parent 61ae0b7642
commit c30afdce02
13 changed files with 410 additions and 28 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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:

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -35,7 +35,7 @@
#include <nvgpu/gr/fecs_trace.h>
#include <nvgpu/gr/gr_utils.h>
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)

View File

@@ -28,14 +28,14 @@
#include <nvgpu/types.h>
#include <nvgpu/list.h>
#include <nvgpu/lock.h>
#include <nvgpu/thread.h>
#include <nvgpu/periodic_timer.h>
/*
* 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;

View File

@@ -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 <linux/async.h>
#include <linux/hrtimer.h>
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

View File

@@ -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 <nvgpu/linux/periodic_timer.h>
#else
#include <nvgpu/posix/periodic_timer.h>
#endif
#include <nvgpu/types.h>
/**
* @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

View File

@@ -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 <nvgpu/cond.h>
#include <signal.h>
#include <time.h>
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

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <nvgpu/bug.h>
#include <nvgpu/periodic_timer.h>
#include <linux/async.h>
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);
}

View File

@@ -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 <nvgpu/cond.h>
#include <nvgpu/periodic_timer.h>
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;
}

View File

@@ -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