/*******************************************************************************
    Copyright (c) 2014 NVidia Corporation

    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.
*******************************************************************************/

//
//    uvm_events.h
//
//    This file contains events API which help to create event queue, configure 
//    it and fetch event information from the UVM module. This interface helps
//    debug/profile UVM.
//
//    Locking Requirements:
//    Uvm user library maintains a global queue state per event queue which
//    facilitates reading events from the library rather than via iocalls.
//    The library doesn't prevent simultaneous updates to this state. It is the
//    user's responsibility to serialize API calls that fetch/skip events from 
//    the event queue. Refer to 'Locking' section of each API for more details.
//
//    Versioning constraint:
//    This header is shared between UVM kernel and tools. To maintain version
//    compatibility with older tools, fields of any structure defined in this
//    file should not be reordered or deleted. Newer fields should be added at
//    the end of the structure.
//
#ifndef _UVM_EVENTS_H_
#define _UVM_EVENTS_H_

#include "uvm-debug.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef enum {
    UvmEventMemoryAccessTypeRead = 0,
    UvmEventMemoryAccessTypeWrite = 1,
    UvmEventMemoryAccessTypeAtomic = 2,
    UvmEventMemoryAccessTypePrefetch = 3
} UvmEventMemoryAccessType;

//
// Event type is used to identify the event data in the event queue
//
typedef enum
{
    UvmEventTypeMemoryViolation = 0,
    UvmEventTypeMigration = 1,
    UvmEventTypeGpuFault = 2,
    UvmEventTypeGpuFaultReplay = 3,
    UvmEventNumTypes = 4,
    UvmEventTypeInvalid = 4,
} UvmEventType;

// Information associated with a memory violation event
typedef struct
{
    //
    // eventType has to be the 1st argument of this structure.
    // Setting eventType = UvmEventTypeMigration helps to identify event data in
    // a queue.
    //
    NvU8 eventType;
    NvU8 accessType;          // read/write violation (UvmEventMemoryAccessType)
    //
    // This structure is shared between UVM kernel and tools.
    // Manually padding the structure so that compiler options like pragma pack 
    // or malign-double will have no effect on the field offsets.
    //
    NvU16 padding16Bits;
    NvU32 padding32Bits;
    NvU64 address;            // faulting address
    NvU64 timeStamp;          // time when violation occurred
    NvU32 pid;                // process id causing violation
    NvU32 threadId;           // thread id causing violation
} UvmEventMemoryViolationInfo;

typedef enum
{
    UvmEventMigrationDirectionCpuToGpu = 0,
    UvmEventMigrationDirectionGpuToCpu = 1
} UvmEventMigrationDirection;

// Information associated with a migration event
typedef struct
{
    //
    // eventType has to be the 1st argument of this structure.
    // Setting eventType = UvmEventMigrationInfo helps to identify event data in
    // a queue.
    //
    NvU8 eventType;
    NvU8 direction;    // direction of migration (UvmEventMigrationDirection)
    //
    // Indices are used for the source and destination of migration instead of
    // using gpu uuid/cpu id. This reduces the size of each event.
    // gpuIndex to gpuUuid relation can be obtained from UvmEventGetGpuUuidTable.
    // Currently we do not distinguish between CPUs so they all use index 0.
    //
    NvU8 srcIndex;                          // source CPU/GPU index
    NvU8 dstIndex;                          // destination CPU/GPU index
    //
    // This structure is shared between UVM kernel and tools.
    // Manually padding the structure so that compiler options like pragma pack 
    // or malign-double will have no effect on the field offsets
    //
    NvU32 padding32Bits;
    NvU64 address;                          // virtual addr used for migration
    NvU64 migratedBytes;                    // number of bytes migrated
    NvU64 beginTimeStamp;                   // migration start time stamp
    NvU64 endTimeStamp;                     // migration end time stamp
    NvU64 streamId;                         // stream causing the migration
} UvmEventMigrationInfo;

typedef enum
{
    UvmFaultTypeInvalidPde = 0,
    UvmFaultTypeInvalidPdeSize = 1,
    UvmFaultTypeInvalidPte = 2,
    UvmFaultTypeLimitViolation = 3,
    UvmFaultTypeUnboundInstBlock = 4,
    UvmFaultTypePrivViolation = 5,
    UvmFaultTypePitchMaskViolation = 6,
    UvmFaultTypeWrite = 7,
    UvmFaultTypeWorkCreation = 8,
    UvmFaultTypeUnsupportedAperture = 9,
    UvmFaultTypeCompressionFailure = 10,
    UvmFaultTypeUnsupportedKind = 11,
    UvmFaultTypeRegionViolation = 12,
    UvmFaultTypePoison = 13,
    UvmFaultTypeAtomic = 14,
} UvmFaultEventType;

//
// This info is provided per gpu fault
// This event can be treated as a start event for gpu fault handling
//
typedef struct
{
    //
    // eventType has to be the 1st argument of this structure.
    // Setting eventType = UvmEventGpuFaultInfo helps to identify event data in
    // a queue.
    //
    NvU8 eventType;
    NvU8 faultType;         // type of gpu fault, refer UvmFaultEventType
    NvU8 accessType;        // memory access type, refer UvmEventMemoryAccessType
    NvU8 reserved8bits;
    NvU32 reserved32bits;
    NvU64 address;          // virtual address at which gpu faulted
    NvU64 timestampCpu;     // time when fault packet entry is read, this is not the
    NvU64 timestampGpu;     // gpu fault timestamp from fault packet
} UvmEventGpuFaultInfo;

//
// This info is provided when a gpu fault is replayed.
// This event can be treated as an end event for gpu fault handling.
// Any other events eg migration events caused as a side-effect of the gpu fault
// would lie between the start and end event. Faults are serviced over a range
// Minimum range = page size (4K/64K/2MB). max would depend on prefetching logic 
// Each start event would have a corresponding end event.
//
typedef struct
{
    //
    // eventType has to be the 1st argument of this structure.
    // Setting eventType = UvmEventGpuFaultReplayInfo helps to identify event 
    // data in a queue.
    //
    NvU8 eventType;
    //
    // This structure is shared between UVM kernel and tools.
    // Manually padding the structure so that compiler options like pragma pack 
    // or malign-double will have no effect on the field offsets
    //
    NvU8  padding8bits;
    NvU16 padding16bits;
    NvU32 padding32bits;
    NvU64 address;          // virtual address at which gpu faulted
    NvU64 timestamp;        // time when gpu fault is detected
} UvmEventGpuFaultReplayInfo;

//
// Entry added in the event queue buffer when an enabled event occurs.
// For compatibility with all tools ensure that this structure is 64 bit aligned.
//
typedef struct
{
    union
    {
        NvU8 eventType;
        UvmEventMemoryViolationInfo memViolation;
        UvmEventMigrationInfo migration;
        UvmEventGpuFaultInfo gpuFault;
        UvmEventGpuFaultReplayInfo gpuFaultReplay;
    } eventData;
} UvmEventEntry;

//
// Type of time stamp used in the event entry:
//
// On windows we support QPC type which uses RDTSC if possible else fallbacks to
// HPET.
// On Linux ClockGetTime provides similar functionality.
// In UvmEventTimeStampTypeAuto the system decides which time stamp best suites
// current environment.
//
typedef enum {
    UvmEventTimeStampTypeWin32QPC = 0,
    UvmEventTimeStampTypePosixClockGetTime = 1,
    UvmEventTimeStampTypeAuto = 2
} UvmEventTimeStampType;

// An opaque queue handle is returned to the user when a queue is created.
typedef NvUPtr UvmEventQueueHandle;

/*******************************************************************************
    UvmEventQueueCreate

    This call creates an event queue of the given size.
    No events are added in the queue till they are enabled by the user.
    Event queue data is visible to the user even after the target process dies 
    if the session is active and queue is not freed.
    
    Arguments:
    
        sessionHandle: (INPUT)
            Handle to the debugging session.

        queueHandle: (OUTPUT)
            Handle to created queue.
        
        queueSize: (INPUT)
            Size of the event queue buffer in units of UvmEventEntry's.
            This quantity must be > 1.

        notificationCount: (INPUT)
            Number of entries after which the user should be notified that 
            there are events to fetch.
            User is notified when queueEntries >= notification count.
    
    Error codes:
        NV_ERR_INSUFFICIENT_PERMISSIONS: If it fails the security check.
        NV_ERR_INVALID_ARGUMENT: If any of the arguments is invalid.
        NV_ERR_INSUFFICIENT_RESOURCES: If not possible to allocate a queue of 
            requested size.
        NV_ERR_BUSY_RETRY: If internal resources are blocked by other threads. 
        NV_ERR_PID_NOT_FOUND: If queue create call is made on a session after
            the target dies.

    Locking: 
        User doesn't need to serialize multiple UvmEventQueueCreate calls as
        each call creates a new queue state associated with the returned queue 
        handle.
*/
NV_STATUS UvmEventQueueCreate(UvmDebugSession sessionHandle,
                              UvmEventQueueHandle *queueHandle,
                              NvS64 queueSize,
                              NvU64 notificationCount,
                              UvmEventTimeStampType timeStampType);

/*******************************************************************************
    UvmEventQueueDestroy
    
    This call frees all the resources associated with the queue
    
    Arguments:
    
        sessionHandle: (INPUT)
            Handle to the debugging session.
        
        queueHandle: (INPUT)
            Handle to the queue which is to be freed

    Error codes:
        RM_ERR_NOT_PERMITTED: If it fails the security check.
        NV_ERR_INVALID_ARGUMENT: If any of the arguments is invalid.
        NV_ERR_BUSY_RETRY: If internal resources are blocked by other threads. 

    Locking:
        User needs to ensure that a queue handle is not deleted while some other 
        thread is using the same queue handle.
*/
NV_STATUS UvmEventQueueDestroy(UvmDebugSession sessionHandle,
                               UvmEventQueueHandle queueHandle);

// Bit flags used to enable/ disable events:
#define UVM_EVENT_ENABLE_MEMORY_VIOLATION       0x1
#define UVM_EVENT_ENABLE_MIGRATION              0x2
#define UVM_EVENT_ENABLE_GPU_ALLOC              0x4
#define UVM_EVENT_ENABLE_CPU_ALLOC              0x8
#define UVM_EVENT_ENABLE_GPU_FAULT              0x10
#define UVM_EVENT_ENABLE_GPU_FAULT_REPLAY       0x20
/*******************************************************************************
    UvmEventEnable

    This call enables a particular event type in the event queue. 
    All events are disabled by default when a queue is created. 

    Arguments:
    
        sessionHandle: (INPUT)
            Handle to the debugging session.
        
        queueHandle: (INPUT)
            Handle to the queue where events are to be enabled

        eventTypeFlags: (INPUT)
            This field specifies the event types to be enabled
            For example: To enable migration events and memory violations:
            pass "UVM_EVENT_ENABLE_MEMORY_VIOLATION |UVM_EVENT_ENABLE_MIGRATION"
            as flags

    Error codes:
        RM_ERR_NOT_PERMITTED: If it fails the security check.
        NV_ERR_INVALID_ARGUMENT: If any of the arguments is invalid. 
        NV_ERR_PID_NOT_FOUND: If this call is made after the target process dies
        NV_ERR_BUSY_RETRY: If internal resources are blocked by other threads. 

    Locking:
        This API does not access the queue state maintained in the user
        library so the user doesn't need to acquire a lock to protect the queue
        state.
*/
NV_STATUS UvmEventEnable(UvmDebugSession sessionHandle,
                         UvmEventQueueHandle queueHandle,
                         unsigned eventTypeFlags);

/*******************************************************************************
    UvmEventDisable

    This call disables a particular event type in the queue.

    Arguments/Error codes/Locking: Refer UvmEventEnable for more details.
*/
NV_STATUS UvmEventDisable(UvmDebugSession sessionHandle,
                          UvmEventQueueHandle queueHandle,
                          unsigned eventTypeFlags);

/*******************************************************************************
    UvmEventWaitOnQueueHandles

    User is notified when queueEntries >= notification count.
    This call does a blocking wait for this notification. It returns when
    at least one of the queue handles has events to be fetched or if it timeouts

    Arguments:

        queueHandles: (INPUT)
            array of queue handles.

        arraySize: (INPUT)
            number of handles in array. 

        timeout: (INPUT)
            timeout in msec

        pNotificationFlags: (OUTPUT)
            If a particular queue handle in the input array is notified then
            the respective bit flag is set in pNotificationFlags.

    Error codes:
        NV_ERR_INVALID_ARGUMENT: If any of the queueHandles is invalid.

    Locking:
        This API accesses constant data maintained in the queue state. Hence, 
        the user doesn't need to acquire a lock to protect the queue state.
*/
NV_STATUS UvmEventWaitOnQueueHandles(UvmEventQueueHandle *queueHandleArray,
                                     unsigned arraySize,
                                     NvU64 timeout,
                                     unsigned *pNotificationFlags);

/*******************************************************************************
    UvmEventGetNotificationHandles

    User is notified when queueEntries >= notification count.
    The user can directly get the queue notification handles rather than using 
    a UVM API to wait on queue handles. This helps the user to wait on other 
    objects (apart from queue notification) along with queue notification
    handles in the same thread. The user can safely use this call along with the
    library supported wait call UvmEventWaitOnQueueHandles.

    Arguments:

        queueHandles: (INPUT)
            array of queue handles.

        arraySize: (INPUT)
            number of handles in array.

        notificationHandles: (OUTPUT)
            Windows: Output of this call contains an array of 'windows event 
                handles' corresponding to the queue handles passes as input.
            Linux: All queues belonging to the same process share the same
                file descriptor(fd) for notification. If the user chooses to use
                UvmEventGetNotificationHandles then he should check all queues
                for new events (by calling UvmEventFetch) when notified on
                the fd.

    Error codes:
        NV_ERR_INVALID_ARGUMENT: If any of the arguments is invalid.

    Locking:
        This API reads constant data maintained in the queue state. Hence, 
        the user doesn't need to acquire a lock to protect the queue state.
*/
NV_STATUS UvmEventGetNotificationHandles(UvmEventQueueHandle *queueHandleArray,
                                         unsigned arraySize,
                                         void* *notificationHandleArray);

/*******************************************************************************
    UvmEventGetGpuUuidTable

    Each migration event entry contains the gpu index to/from where data is 
    migrated. This index maps to a corresponding gpu uuid in the gpuUuidTable. 
    Using indices saves on the size of each event entry. This API provides the
    gpuIndex to gpuUuid relation to the user. 

    Arguments:

        gpuUuidTable: (OUTPUT)
            The return value is an array of uuid’s. The array index is the
            corresponding gpuIndex. There can be at max 32 gpus associated with
            UVM, so array size is 32. 

        validCount: (OUTPUT)
            The system doesn't normally contain 32 GPUs. This field gives the
            count of entries that are valid in the returned gpuUuidTable.

    Error codes:
        NV_ERR_BUSY_RETRY: If internal resources are blocked by other threads. 

    Locking: 
        This API does not access the queue state maintained in the user
        library and so the user doesn't need to acquire a lock to protect the
        queue state.
*/
NV_STATUS UvmEventGetGpuUuidTable(UvmGpuUuid *gpuUuidTable,
                                  unsigned *validCount);

/*******************************************************************************
    UvmEventFetch

    This call is used to fetch the queue entries in a user buffer.

    Arguments:
    
        sessionHandle: (INPUT)
            Handle to the debugging session.
        
        queueHandle: (INPUT)
            queue from where to fetch the events.

        pBuffer: (OUTPUT)
            Pointer to the buffer where the API will copy the events. User
            shall ensure the size is enough.

        nEntries: (INPUT/OUTPUT)
            It provides the maximum number of entries that will be fetched
            from the queue. If this number is larger than the size of the
            queue it will be internally capped to that value.
            As output it returns the actual number of entries copies to the
            buffer.
    
    Error codes:
        RM_ERR_NOT_PERMITTED: If it fails the security check.
        NV_ERR_INVALID_ARGUMENT: If any of the arguments is invalid.
        NV_ERR_INVALID_INDEX: The indices of the queue have been corrupted.
        NV_ERR_BUFFER_TOO_SMALL: The event queue buffer provided by the caller
        was too small to contain all of the events that occurred during this run.
        Events were therefore dropped (not recorded). Please re-run with a
        larger buffer.

    Locking: 
        This API updates the queue state. Hence simultaneous calls to fetch/
        skip events should be avoided as that might corrupt the queue state.
*/
NV_STATUS UvmEventFetch2(UvmDebugSession sessionHandle,
                        UvmEventQueueHandle queueHandle,
                        UvmEventEntry *pBuffer,
                        NvS64 *nEntries);
/* HACK: Revert once this code is in cuda_a */
NV_STATUS UvmEventFetch(UvmDebugSession sessionHandle,
                        UvmEventQueueHandle queueHandle,
                        UvmEventEntry *pBuffer,
                        NvU64 *nEntries);

/*******************************************************************************
    UvmEventSkipAll
    
    This API drops all event entries from the queue.

    Arguments:

        sessionHandle: (INPUT)
            Handle to the debugging session.

        queueHandle: (INPUT)
            target queue.
    
    Error codes:
        RM_ERR_NOT_PERMITTED: If it fails the security check.
        NV_ERR_INVALID_ARGUMENT: If any of the arguments is invalid.

    Locking: 
        This API updates the queue state. Hence simultaneous calls to fetch/
        skip events should be avoided as that might corrupt the queue state.
*/
NV_STATUS UvmEventSkipAll(UvmDebugSession sessionHandle,
                          UvmEventQueueHandle queueHandle);
/* HACK: Revert once this code is in cuda_a */
NV_STATUS UvmEventSkip(UvmDebugSession sessionHandle,
                       UvmEventQueueHandle queueHandle);

/*******************************************************************************
    UvmEventQueryTimeStampType

    This API returns the type of time stamp used in an event entry for a given
    queue.

    Arguments:

        sessionHandle: (INPUT)
            Handle to the debugging session.

        queueHandle: (INPUT)
            target queue.

        timeStampType: (OUTPUT)
            type of time stamp used in event entry. See UvmEventTimestampType
            for supported types of time stamps.

    Error codes:
        RM_ERR_NOT_PERMITTED: If it fails the security check.
        NV_ERR_INVALID_ARGUMENT: If any of the arguments is invalid.

    Locking:
        This API reads constant data maintained in the queue state. Hence,
        the user doesn't need to acquire a lock to protect the queue state.
*/
NV_STATUS UvmEventQueryTimeStampType(UvmDebugSession sessionHandle,
                                      UvmEventQueueHandle queueHandle,
                                      UvmEventTimeStampType *timeStampType);

#ifdef __cplusplus
}
#endif

#endif // _UVM_EVENTS_H_
