mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2024-12-13 23:48:48 +01:00
377 lines
14 KiB
C
377 lines
14 KiB
C
|
/*******************************************************************************
|
||
|
Copyright (c) 2016-2021 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.
|
||
|
|
||
|
*******************************************************************************/
|
||
|
|
||
|
#ifndef __UVM_PERF_EVENTS_H__
|
||
|
#define __UVM_PERF_EVENTS_H__
|
||
|
|
||
|
#include "uvm_linux.h"
|
||
|
#include "uvm_forward_decl.h"
|
||
|
#include "uvm_processors.h"
|
||
|
#include "uvm_hal_types.h"
|
||
|
#include "uvm_lock.h"
|
||
|
#include "uvm_va_block_types.h"
|
||
|
|
||
|
// uvm_perf_events is an event notification dispatcher that broadcasts events
|
||
|
// to clients. Clients register functions to be called under specific events.
|
||
|
// The callback lists are stored per va_space and, therefore, different
|
||
|
// callbacks can be registered per client. This will be useful to use
|
||
|
// different performance heuristic implementations depending on the GPU
|
||
|
// resources used by each process. For example, on a system with Pascal +
|
||
|
// Maxwell GPUs, VA spaces which have Maxwell GPU VA spaces will be restrited
|
||
|
// to the UVM-Lite feature set, while a VA space which only uses the Pascal
|
||
|
// GPU will not be downgraded. Registering/unregistering callbacks requires
|
||
|
// holding the VA space events lock in write mode. The exact locking
|
||
|
// guarantees under which callbacks are executed depend on the specific event,
|
||
|
// but the VA space events lock is held in read mode for all of them. The
|
||
|
// additional locking guarantees are defined in each event definition.
|
||
|
|
||
|
// Performance-related events that can be notified
|
||
|
typedef enum
|
||
|
{
|
||
|
// Locking: uvm_va_space: at least in read mode, uvm_va_block: exclusive / nobody is referencing the block anymore
|
||
|
UVM_PERF_EVENT_BLOCK_DESTROY = 0,
|
||
|
|
||
|
// Locking: uvm_va_space: write
|
||
|
UVM_PERF_EVENT_BLOCK_SHRINK,
|
||
|
|
||
|
// Locking: uvm_va_space: write
|
||
|
UVM_PERF_EVENT_RANGE_DESTROY,
|
||
|
|
||
|
// Locking: uvm_va_space: write
|
||
|
UVM_PERF_EVENT_RANGE_SHRINK,
|
||
|
|
||
|
// Locking: uvm_va_space: write
|
||
|
UVM_PERF_EVENT_MODULE_UNLOAD,
|
||
|
|
||
|
// Locking: uvm_va_space: at least in read mode, uvm_va_block: exclusive (if uvm_va_block is not NULL)
|
||
|
UVM_PERF_EVENT_FAULT,
|
||
|
|
||
|
// Locking: uvm_va_block: exclusive. Notably the uvm_va_space lock may not be held on eviction.
|
||
|
UVM_PERF_EVENT_MIGRATION,
|
||
|
|
||
|
// Locking: uvm_va_space: at least in read mode, uvm_va_block: exclusive
|
||
|
UVM_PERF_EVENT_REVOCATION,
|
||
|
|
||
|
UVM_PERF_EVENT_COUNT,
|
||
|
} uvm_perf_event_t;
|
||
|
|
||
|
// Format of the data passed to callbacks. Scope must be filled with the appropriate values by the code which notifies
|
||
|
// the event
|
||
|
typedef union
|
||
|
{
|
||
|
struct
|
||
|
{
|
||
|
uvm_va_block_t *block;
|
||
|
} block_destroy;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
uvm_va_block_t *block;
|
||
|
} block_shrink;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
uvm_va_range_t *range;
|
||
|
} range_destroy;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
uvm_va_range_t *range;
|
||
|
} range_shrink;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
uvm_perf_module_t *module;
|
||
|
|
||
|
// Only one of these two can be set. The other one must be NULL
|
||
|
uvm_va_block_t *block;
|
||
|
uvm_va_range_t *range;
|
||
|
} module_unload;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
// This field contains the VA space where this fault was reported.
|
||
|
// If block is not NULL, this field must match
|
||
|
// uvm_va_block_get_va_space(block).
|
||
|
uvm_va_space_t *space;
|
||
|
|
||
|
// VA block for the page where the fault was triggered if it exists,
|
||
|
// NULL otherwise (this can happen if the fault is fatal or the
|
||
|
// VA block could not be created).
|
||
|
uvm_va_block_t *block;
|
||
|
|
||
|
// ID of the faulting processor
|
||
|
uvm_processor_id_t proc_id;
|
||
|
|
||
|
// ID of the preferred location processor
|
||
|
uvm_processor_id_t preferred_location;
|
||
|
|
||
|
// Fault descriptor
|
||
|
union
|
||
|
{
|
||
|
struct
|
||
|
{
|
||
|
uvm_fault_buffer_entry_t *buffer_entry;
|
||
|
|
||
|
NvU32 batch_id;
|
||
|
|
||
|
bool is_duplicate;
|
||
|
} gpu;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
NvU64 fault_va;
|
||
|
|
||
|
bool is_write;
|
||
|
|
||
|
NvU64 pc;
|
||
|
} cpu;
|
||
|
};
|
||
|
} fault;
|
||
|
|
||
|
// This event is emitted during migration and the residency bits may be
|
||
|
// stale. Do not rely on them in the callbacks.
|
||
|
struct
|
||
|
{
|
||
|
uvm_push_t *push;
|
||
|
uvm_va_block_t *block;
|
||
|
|
||
|
// ID of the destination processor of the migration
|
||
|
uvm_processor_id_t dst;
|
||
|
|
||
|
// ID of the source processor of the migration
|
||
|
uvm_processor_id_t src;
|
||
|
|
||
|
// Start address of the memory range being migrated
|
||
|
NvU64 address;
|
||
|
|
||
|
// Number of bytes being migrated
|
||
|
NvU64 bytes;
|
||
|
|
||
|
// Whether the page has been copied or moved
|
||
|
uvm_va_block_transfer_mode_t transfer_mode;
|
||
|
|
||
|
// Event that performed the call to make_resident
|
||
|
uvm_make_resident_cause_t cause;
|
||
|
|
||
|
// Pointer to the make_resident context from the va_block_context
|
||
|
// struct used by the operation that triggered the make_resident call.
|
||
|
uvm_make_resident_context_t *make_resident_context;
|
||
|
} migration;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
uvm_va_block_t *block;
|
||
|
|
||
|
// ID of the processor whose access permissions have been revoked
|
||
|
uvm_processor_id_t proc_id;
|
||
|
|
||
|
// Start address of the memory range being revoked
|
||
|
NvU64 address;
|
||
|
|
||
|
// Number of bytes of the memory range being revoked
|
||
|
NvU64 bytes;
|
||
|
|
||
|
// Old access permission
|
||
|
uvm_prot_t old_prot;
|
||
|
|
||
|
// New access permission
|
||
|
uvm_prot_t new_prot;
|
||
|
} revocation;
|
||
|
} uvm_perf_event_data_t;
|
||
|
|
||
|
// Type of the function that can be registered as a callback
|
||
|
//
|
||
|
// event_id: is the event being notified. Passing it to the callback enables using the same function to handle
|
||
|
// different events.
|
||
|
// event_data: extra event data that is passed to the callback function. The format of data passed for each event type
|
||
|
// is declared in the uvm_perf_event_data_t union
|
||
|
typedef void (*uvm_perf_event_callback_t)(uvm_perf_event_t event_id, uvm_perf_event_data_t *event_data);
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
// Lock protecting the events
|
||
|
//
|
||
|
// Held for write during registration/unregistration of callbacks and for
|
||
|
// read during notification of events.
|
||
|
//
|
||
|
// Also used by tools to protect their state and registration of perf event callbacks.
|
||
|
uvm_rw_semaphore_t lock;
|
||
|
|
||
|
// Array of callbacks for event notification
|
||
|
struct list_head event_callbacks[UVM_PERF_EVENT_COUNT];
|
||
|
|
||
|
uvm_va_space_t *va_space;
|
||
|
} uvm_perf_va_space_events_t;
|
||
|
|
||
|
// Initialize event notifiction for a va_space. This must be called from va_space construction. No locking required
|
||
|
NV_STATUS uvm_perf_init_va_space_events(uvm_va_space_t *va_space, uvm_perf_va_space_events_t *va_space_events);
|
||
|
|
||
|
// Finalize event notifiction for a va_space. Caller must hold va_space lock in write mode
|
||
|
void uvm_perf_destroy_va_space_events(uvm_perf_va_space_events_t *va_space_events);
|
||
|
|
||
|
// Register a callback to be executed under the given event. The given callback cannot have been already registered for
|
||
|
// the same event, although the same callback can be registered for different events.
|
||
|
NV_STATUS uvm_perf_register_event_callback(uvm_perf_va_space_events_t *va_space_events,
|
||
|
uvm_perf_event_t event_id, uvm_perf_event_callback_t callback);
|
||
|
|
||
|
// Same as uvm_perf_register_event_callback(), but the caller must hold
|
||
|
// va_space_events lock in write mode.
|
||
|
NV_STATUS uvm_perf_register_event_callback_locked(uvm_perf_va_space_events_t *va_space_events,
|
||
|
uvm_perf_event_t event_id, uvm_perf_event_callback_t callback);
|
||
|
|
||
|
// Removes a callback for the given event. It's safe to call with a callback that hasn't been registered.
|
||
|
void uvm_perf_unregister_event_callback(uvm_perf_va_space_events_t *va_space_events, uvm_perf_event_t event_id,
|
||
|
uvm_perf_event_callback_t callback);
|
||
|
|
||
|
// Same as uvm_perf_unregister_event_callback(), but the caller must hold
|
||
|
// va_space_events lock in write mode.
|
||
|
void uvm_perf_unregister_event_callback_locked(uvm_perf_va_space_events_t *va_space_events, uvm_perf_event_t event_id,
|
||
|
uvm_perf_event_callback_t callback);
|
||
|
|
||
|
// Invoke the callbacks registered for the given event. Callbacks cannot fail.
|
||
|
// Acquires the va_space_events lock internally
|
||
|
void uvm_perf_event_notify(uvm_perf_va_space_events_t *va_space_events, uvm_perf_event_t event_id,
|
||
|
uvm_perf_event_data_t *event_data);
|
||
|
|
||
|
// Checks if the given callback is already registered for the event.
|
||
|
// va_space_events.lock must be held in either mode by the caller.
|
||
|
bool uvm_perf_is_event_callback_registered(uvm_perf_va_space_events_t *va_space_events,
|
||
|
uvm_perf_event_t event_id,
|
||
|
uvm_perf_event_callback_t callback);
|
||
|
|
||
|
// Initialization/cleanup functions
|
||
|
NV_STATUS uvm_perf_events_init(void);
|
||
|
void uvm_perf_events_exit(void);
|
||
|
|
||
|
// Helper to notify migration events
|
||
|
static inline void uvm_perf_event_notify_migration(uvm_perf_va_space_events_t *va_space_events,
|
||
|
uvm_push_t *push,
|
||
|
uvm_va_block_t *va_block,
|
||
|
uvm_processor_id_t dst,
|
||
|
uvm_processor_id_t src,
|
||
|
NvU64 address,
|
||
|
NvU64 bytes,
|
||
|
uvm_va_block_transfer_mode_t transfer_mode,
|
||
|
uvm_make_resident_cause_t cause,
|
||
|
uvm_make_resident_context_t *make_resident_context)
|
||
|
{
|
||
|
uvm_perf_event_data_t event_data =
|
||
|
{
|
||
|
.migration =
|
||
|
{
|
||
|
.push = push,
|
||
|
.block = va_block,
|
||
|
.dst = dst,
|
||
|
.src = src,
|
||
|
.address = address,
|
||
|
.bytes = bytes,
|
||
|
.transfer_mode = transfer_mode,
|
||
|
.cause = cause,
|
||
|
.make_resident_context = make_resident_context,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
uvm_perf_event_notify(va_space_events, UVM_PERF_EVENT_MIGRATION, &event_data);
|
||
|
}
|
||
|
|
||
|
// Helper to notify gpu fault events
|
||
|
static inline void uvm_perf_event_notify_gpu_fault(uvm_perf_va_space_events_t *va_space_events,
|
||
|
uvm_va_block_t *va_block,
|
||
|
uvm_gpu_id_t gpu_id,
|
||
|
uvm_processor_id_t preferred_location,
|
||
|
uvm_fault_buffer_entry_t *buffer_entry,
|
||
|
NvU32 batch_id,
|
||
|
bool is_duplicate)
|
||
|
{
|
||
|
uvm_perf_event_data_t event_data =
|
||
|
{
|
||
|
.fault =
|
||
|
{
|
||
|
.space = va_space_events->va_space,
|
||
|
.block = va_block,
|
||
|
.proc_id = gpu_id,
|
||
|
.preferred_location = preferred_location,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
event_data.fault.gpu.buffer_entry = buffer_entry;
|
||
|
event_data.fault.gpu.batch_id = batch_id;
|
||
|
event_data.fault.gpu.is_duplicate = is_duplicate;
|
||
|
|
||
|
uvm_perf_event_notify(va_space_events, UVM_PERF_EVENT_FAULT, &event_data);
|
||
|
}
|
||
|
|
||
|
// Helper to notify cpu fault events
|
||
|
static inline void uvm_perf_event_notify_cpu_fault(uvm_perf_va_space_events_t *va_space_events,
|
||
|
uvm_va_block_t *va_block,
|
||
|
uvm_processor_id_t preferred_location,
|
||
|
NvU64 fault_va,
|
||
|
bool is_write,
|
||
|
NvU64 pc)
|
||
|
{
|
||
|
uvm_perf_event_data_t event_data =
|
||
|
{
|
||
|
.fault =
|
||
|
{
|
||
|
.space = va_space_events->va_space,
|
||
|
.block = va_block,
|
||
|
.proc_id = UVM_ID_CPU,
|
||
|
.preferred_location = preferred_location,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
event_data.fault.cpu.fault_va = fault_va,
|
||
|
event_data.fault.cpu.is_write = is_write,
|
||
|
event_data.fault.cpu.pc = pc,
|
||
|
|
||
|
uvm_perf_event_notify(va_space_events, UVM_PERF_EVENT_FAULT, &event_data);
|
||
|
}
|
||
|
|
||
|
// Helper to notify permission revocation
|
||
|
static inline void uvm_perf_event_notify_revocation(uvm_perf_va_space_events_t *va_space_events,
|
||
|
uvm_va_block_t *va_block,
|
||
|
uvm_processor_id_t id,
|
||
|
NvU64 addr,
|
||
|
NvU64 bytes,
|
||
|
uvm_prot_t old_prot,
|
||
|
uvm_prot_t new_prot)
|
||
|
{
|
||
|
uvm_perf_event_data_t event_data =
|
||
|
{
|
||
|
.revocation =
|
||
|
{
|
||
|
.block = va_block,
|
||
|
.proc_id = id,
|
||
|
.address = addr,
|
||
|
.bytes = bytes,
|
||
|
.old_prot = old_prot,
|
||
|
.new_prot = new_prot,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
uvm_perf_event_notify(va_space_events, UVM_PERF_EVENT_REVOCATION, &event_data);
|
||
|
}
|
||
|
|
||
|
#endif
|