/*
 * SPDX-FileCopyrightText: Copyright (c) 1993-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.
 */
#ifndef LOCKS_H
#define LOCKS_H

#include "core/core.h"
#include "os/os.h"

// Forward declarations
typedef struct OBJSYS OBJSYS;

typedef enum
{
    GPU_LOCK_GRP_SUBDEVICE,  // locks will be taken for subdevice only
    GPU_LOCK_GRP_DEVICE,     // locks will be taken for device only
    GPU_LOCK_GRP_MASK,       // locks will be taken for devices specified by the mask
    GPU_LOCK_GRP_ALL         // locks will be taken for all devices
} GPU_LOCK_GRP_ID;
typedef NvU32 GPU_MASK;

//
// This structure is used to trace lock acquire/release activity.
// The calling IP is stored in a circular array.
//
#define MAX_TRACE_LOCK_CALLS           32

typedef enum
{
    lockTraceEmpty,
    lockTraceAcquire,
    lockTraceRelease,
    lockTraceAlloc,
    lockTraceFree
} LOCK_TRACE_TYPE;

typedef struct
{
    LOCK_TRACE_TYPE type;
    union {
        GPU_MASK gpuMask; // For GPU locks
        NvU32 lockModule; // For API lock
        NvU32 value;
    } data32;
    union {
        NvU16 gpuInst;    // For GPU locks
        NvU16 lockFlags;  // For API lock
        NvU16 value;
    } data16;
    NvBool bHighIrql;
    NvU8   priority;
    NvU64  callerRA;
    NvU64  threadId;
    NvU64  timestamp;
} LOCK_TRACE_ENTRY;

typedef struct
{
    LOCK_TRACE_ENTRY entries[MAX_TRACE_LOCK_CALLS];
    NvU32 index;
} LOCK_TRACE_INFO;

#define INSERT_LOCK_TRACE(plti, ra, t, d16, d32, ti, irql, pr, ts) \
{                                                                  \
    (plti)->entries[(plti)->index].callerRA = (NvUPtr)ra;          \
    (plti)->entries[(plti)->index].type = t;                       \
    (plti)->entries[(plti)->index].data16.value = d16;             \
    (plti)->entries[(plti)->index].data32.value = d32;             \
    (plti)->entries[(plti)->index].threadId = ti;                  \
    (plti)->entries[(plti)->index].timestamp = ts;                 \
    (plti)->entries[(plti)->index].bHighIrql = irql;               \
    (plti)->entries[(plti)->index].priority = pr;                  \
    (plti)->index = ((plti)->index + 1) % MAX_TRACE_LOCK_CALLS;    \
}

//
// Callers specify this value when they to lock all possible GPUs.
//
#define GPUS_LOCK_ALL                                   (0xFFFFFFFF)

//
// Flags for rmGpusLock[Acquire,Release] operations.
//

// default no flags
#define GPUS_LOCK_FLAGS_NONE                            (0x00000000)
// conditional acquire; if lock is already held then return error
#define GPU_LOCK_FLAGS_COND_ACQUIRE                     NVBIT(0)
// acquire the lock in read (shared) mode, if applicable
#define GPU_LOCK_FLAGS_READ                             NVBIT(1)
// Attempt acquire even if it potentially violates the locking order
// But do not block in a way that could cause a deadlock
#define GPU_LOCK_FLAGS_SAFE_LOCK_UPGRADE                NVBIT(2)

//
// RM Lock Related Functions
//
NV_STATUS  rmLocksAlloc(OBJSYS *);
void       rmLocksFree(OBJSYS *);

NV_STATUS  rmLocksAcquireAll(NvU32 module);
void       rmLocksReleaseAll(void);

NV_STATUS   workItemLocksAcquire(NvU32 gpuInstance, NvU32 flags, NvU32 *pReleaseLocks, NvU32 *pGpuMask);
void        workItemLocksRelease(NvU32 releaseLocks, NvU32 gpuMask);

//
// Thread priority boosting and throttling:
//   Used to temporarily increase the priority of a thread on Windows platforms
//   in order to prevent starvation from the scheduler.
//
void        threadPriorityStateAlloc(void);
void        threadPriorityStateFree(void);

//! Temporarily boost the priority of the current thread
void        threadPriorityBoost(NvU64* pBoostPriority, NvU64 *pOriginalPriority);

//! Gradually lower the priority of the current thread if it is boosted and sufficient time has elapsed
void        threadPriorityThrottle(void);

//! Restore the original priority of the current thread if it is boosted
void        threadPriorityRestore(void);

NV_STATUS  rmGpuGroupLockGetMask(NvU32 gpuInst, GPU_LOCK_GRP_ID gpuGrpId, GPU_MASK* pGpuMask);

//
// Defines for rmGpuLockSetOwner operation.
//
#define GPUS_LOCK_OWNER_PENDING_DPC_REFRESH             (OS_THREAD_HANDLE)(-1)

NV_STATUS  rmGpuLockInfoInit(void);
void       rmGpuLockInfoDestroy(void);
NV_STATUS  rmGpuLockAlloc(NvU32);
void       rmGpuLockFree(NvU32);
NV_STATUS  rmGpuLocksAcquire(NvU32, NvU32);
NvU32      rmGpuLocksRelease(NvU32, OBJGPU *);
void       rmGpuLocksFreeze(GPU_MASK);
void       rmGpuLocksUnfreeze(GPU_MASK);
NV_STATUS  rmGpuLockHide(NvU32);
void       rmGpuLockShow(NvU32);
NvBool     rmGpuLockIsOwner(void);
NvU32      rmGpuLocksGetOwnedMask(void);
NvBool     rmGpuLockIsHidden(OBJGPU *);
NV_STATUS  rmGpuLockSetOwner(OS_THREAD_HANDLE);
NV_STATUS  rmGpuGroupLockAcquire(NvU32, GPU_LOCK_GRP_ID, NvU32, NvU32, GPU_MASK *);
NV_STATUS  rmGpuGroupLockRelease(GPU_MASK, NvU32);
NvBool     rmGpuGroupLockIsOwner(NvU32, GPU_LOCK_GRP_ID, GPU_MASK*);

NvBool     rmDeviceGpuLockIsOwner(NvU32);
NV_STATUS  rmDeviceGpuLockSetOwner(OBJGPU *, OS_THREAD_HANDLE);
NV_STATUS  rmDeviceGpuLocksAcquire(OBJGPU *, NvU32, NvU32);
NvU32      rmDeviceGpuLocksRelease(OBJGPU *, NvU32, OBJGPU *);

NV_STATUS  rmIntrMaskLockAlloc(NvU32 gpuInst);
void       rmIntrMaskLockFree(NvU32 gpuInst);
/// @note The return value is always zero, not the actual IRQL
NvU64      rmIntrMaskLockAcquire(OBJGPU *pGpu);
void       rmIntrMaskLockRelease(OBJGPU *pGpu, NvU64 oldIrql);

// wrappers for handling lock-related NV_ASSERT_OR_RETURNs
#define LOCK_ASSERT_AND_RETURN(cond)                    NV_ASSERT_OR_ELSE_STR((cond), #cond, return NV_ERR_INVALID_LOCK_STATE)
#define IRQL_ASSERT_AND_RETURN(cond)                    NV_ASSERT_OR_ELSE_STR((cond), #cond, return NV_ERR_INVALID_IRQ_LEVEL)
#define LOCK_ASSERT_AND_RETURN_BOOL(cond, bRet)         NV_ASSERT_OR_ELSE_STR((cond), #cond, return (bRet))

#define LOCK_METER_OP(f,l,t,d0,d1,d2)
#define LOCK_METER_DATA(t,d0,d1,d2)

#define rmInitLockMetering()
#define rmDestroyLockMetering()

#include "rmapi/rmapi.h"

#define API_LOCK_FLAGS_NONE                             RMAPI_LOCK_FLAGS_NONE
#define API_LOCK_FLAGS_COND_ACQUIRE                     RMAPI_LOCK_FLAGS_COND_ACQUIRE

#endif // LOCKS_H
