/* * SPDX-FileCopyrightText: Copyright (c) 2020-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: MIT * * 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. */ #define __NO_VERSION__ #include // For container_of #include #include #include #include "os-interface.h" #include "nv-linux.h" #if !defined(NVCPU_PPC64LE) #define NV_NANO_TIMER_USE_HRTIMER 1 #else #define NV_NANO_TIMER_USE_HRTIMER 0 #endif // !defined(NVCPU_PPC64LE) struct nv_nano_timer { #if NV_NANO_TIMER_USE_HRTIMER struct hrtimer hr_timer; // This parameter holds linux high resolution timer object // can get replaced with platform specific timer object #else struct timer_list jiffy_timer; #endif nv_linux_state_t *nv_linux_state; void (*nv_nano_timer_callback)(struct nv_nano_timer *nv_nstimer); void *pTmrEvent; }; /*! * @brief runs nano second resolution timer callback * * @param[in] nv_nstimer Pointer to nv_nano_timer_t object */ static void nvidia_nano_timer_callback( nv_nano_timer_t *nv_nstimer) { nv_state_t *nv = NULL; nv_linux_state_t *nvl = nv_nstimer->nv_linux_state; nvidia_stack_t *sp = NULL; if (nv_kmem_cache_alloc_stack(&sp) != 0) { nv_printf(NV_DBG_ERRORS, "NVRM: no cache memory \n"); return; } nv = NV_STATE_PTR(nvl); if (rm_run_nano_timer_callback(sp, nv, nv_nstimer->pTmrEvent) != NV_OK) { nv_printf(NV_DBG_ERRORS, "NVRM: Error in service of callback \n"); } nv_kmem_cache_free_stack(sp); } /*! * @brief Allocates nano second resolution timer object * * @returns nv_nano_timer_t allocated pointer */ static nv_nano_timer_t *nv_alloc_nano_timer(void) { nv_nano_timer_t *nv_nstimer; NV_KMALLOC(nv_nstimer, sizeof(nv_nano_timer_t)); if (nv_nstimer == NULL) { return NULL; } memset(nv_nstimer, 0, sizeof(nv_nano_timer_t)); return nv_nstimer; } #if NV_NANO_TIMER_USE_HRTIMER static enum hrtimer_restart nv_nano_timer_callback_typed_data(struct hrtimer *hrtmr) { struct nv_nano_timer *nv_nstimer = container_of(hrtmr, struct nv_nano_timer, hr_timer); nv_nstimer->nv_nano_timer_callback(nv_nstimer); return HRTIMER_NORESTART; } #else static inline void nv_jiffy_timer_callback_typed_data(struct timer_list *timer) { struct nv_nano_timer *nv_nstimer = container_of(timer, struct nv_nano_timer, jiffy_timer); nv_nstimer->nv_nano_timer_callback(nv_nstimer); } static inline void nv_jiffy_timer_callback_anon_data(unsigned long arg) { struct nv_nano_timer *nv_nstimer = (struct nv_nano_timer *)arg; nv_nstimer->nv_nano_timer_callback(nv_nstimer); } #endif /*! * @brief Creates & initializes nano second resolution timer object * * @param[in] nv Per gpu linux state * @param[in] tmrEvent pointer to TMR_EVENT * @param[in] nv_nstimer Pointer to nv_nano_timer_t object */ void NV_API_CALL nv_create_nano_timer( nv_state_t *nv, void *pTmrEvent, nv_nano_timer_t **pnv_nstimer) { nv_linux_state_t *nvl = NV_GET_NVL_FROM_NV_STATE(nv); nv_nano_timer_t *nv_nstimer = nv_alloc_nano_timer(); if (nv_nstimer == NULL) { nv_printf(NV_DBG_ERRORS, "NVRM: Not able to create timer object \n"); *pnv_nstimer = NULL; return; } nv_nstimer->nv_linux_state = nvl; nv_nstimer->pTmrEvent = pTmrEvent; nv_nstimer->nv_nano_timer_callback = nvidia_nano_timer_callback; #if NV_NANO_TIMER_USE_HRTIMER hrtimer_init(&nv_nstimer->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); nv_nstimer->hr_timer.function = nv_nano_timer_callback_typed_data; #else #if defined(NV_TIMER_SETUP_PRESENT) timer_setup(&nv_nstimer->jiffy_timer, nv_jiffy_timer_callback_typed_data, 0); #else init_timer(&nv_nstimer->jiffy_timer); nv_nstimer->jiffy_timer.function = nv_jiffy_timer_callback_anon_data; nv_nstimer->jiffy_timer.data = (unsigned long)nv_nstimer; #endif // NV_TIMER_SETUP_PRESENT #endif // NV_NANO_TIMER_USE_HRTIMER *pnv_nstimer = nv_nstimer; } /*! * @brief Starts nano second resolution timer * * @param[in] nv Per gpu linux state * @param[in] nv_nstimer Pointer to nv_nano_timer_t object * @param[in] timens time in nano seconds */ void NV_API_CALL nv_start_nano_timer( nv_state_t *nv, nv_nano_timer_t *nv_nstimer, NvU64 time_ns) { #if NV_NANO_TIMER_USE_HRTIMER ktime_t ktime = ktime_set(0, time_ns); hrtimer_start(&nv_nstimer->hr_timer, ktime, HRTIMER_MODE_REL); #else unsigned long time_jiffies; NvU32 time_us; time_us = (NvU32)(time_ns / 1000); if (time_us == 0) { nv_printf(NV_DBG_WARNINGS, "NVRM: Timer value cannot be less than 1 usec.\n"); } time_jiffies = usecs_to_jiffies(time_us); mod_timer(&nv_nstimer->jiffy_timer, jiffies + time_jiffies); #endif } /*! * @brief Cancels nano second resolution timer * * @param[in] nv Per gpu linux state * @param[in] nv_nstimer Pointer to nv_nano_timer_t object */ void NV_API_CALL nv_cancel_nano_timer( nv_state_t *nv, nv_nano_timer_t *nv_nstimer) { #if NV_NANO_TIMER_USE_HRTIMER hrtimer_cancel(&nv_nstimer->hr_timer); #else del_timer(&nv_nstimer->jiffy_timer); #endif } /*! * @brief Cancels & deletes nano second resolution timer object * * @param[in] nv Per gpu linux state * @param[in] nv_nstimer Pointer to nv_nano_timer_t object */ void NV_API_CALL nv_destroy_nano_timer( nv_state_t *nv, nv_nano_timer_t *nv_nstimer) { nv_cancel_nano_timer(nv, nv_nstimer); NV_KFREE(nv_nstimer, sizeof(nv_nano_timer_t)); }