Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions projects/rocr-runtime/libhsakmt/include/impl/wddm/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include "impl/wddm/types.h"
#include "impl/wddm/gpu_memory.h"
#include "impl/wddm/cmd_util.h"
#include "hsakmt/hsakmttypes.h"

namespace wsl {
namespace thunk {
Expand Down Expand Up @@ -252,6 +253,10 @@ class WDDMDevice {
private:
bool Escape(void* priv_data, uint32_t priv_size, bool hw_access) const;
NTSTATUS ParseDeviceInfo(void);
uint64_t AllocateCwsrSize(uint64_t* out_ctx_size = nullptr, uint64_t* out_debug_size = nullptr) const;
void FillCwsrHeader(void* cpu_addr, uint64_t ctx_save_restore_size,
uint64_t debug_memory_size, uint32_t num_xcc,
volatile HSAint64* error_reason, HSAuint32 error_event_id);
void DestroyDeviceInfo(void);
bool CreateDevice(void);
bool DestroyDevice(void);
Expand Down
8 changes: 7 additions & 1 deletion projects/rocr-runtime/libhsakmt/include/impl/wddm/queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ class WDDMQueue {
std::atomic<uint64_t>* ring_rptr = nullptr;

uint32_t aql_doorbell_offset_ = 0; //!< Doorbell offset for this AQL queue

GpuMemoryHandle cwsr_mem_ = nullptr; //!< CWSR (Context Wave Save/Restore) memory allocation
D3DKMT_HANDLE cwsr_mem_handle_ = 0; //!< KMT allocation handle of CWSR region (passed as CwsrMemHandle)
volatile int64_t* error_reason_ = nullptr; //!< ErrorReason payload ptr (QueueResource::ErrorReason)
HSAuint32 error_event_id_ = 0; //!< ErrorEventId from HsaEvent::EventId (0 if no event)
};

class ComputeQueue : public WDDMQueue {
Expand All @@ -153,7 +158,8 @@ class ComputeQueue : public WDDMQueue {
volatile int64_t *error_addr,
uint32_t cmdbuf_size,
uint32_t engine,
bool use_hws = true);
bool use_hws = true,
HSAuint32 event_id = 0);

~ComputeQueue();

Expand Down
4 changes: 2 additions & 2 deletions projects/rocr-runtime/libhsakmt/src/dxg/queues.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ HSAKMT_STATUS HSAKMTAPI hsaKmtCreateQueueV2(HSAuint32 NodeId,
HSAKMT_STATUS result;

CHECK_DXG_OPEN();
assert(Event == nullptr);
Comment thread
gandryey marked this conversation as resolved.
Resolved

#if defined(__linux__)
// Defer resolving libhsa-runtime64 symbols until a queue is actually needed.
Expand Down Expand Up @@ -117,13 +116,14 @@ HSAKMT_STATUS HSAKMTAPI hsaKmtCreateQueueV2(HSAuint32 NodeId,
uint32_t cmdbuf_size = device_->GetCmdbufSize();
uint32_t queue_engine = device_->GetComputeEngine();
bool use_hws = device_->IsHwsEnabled(queue_engine);
HSAuint32 event_id = Event ? Event->EventId : 0;
auto queue_ = new wsl::thunk::ComputeQueue(
device_, QueueAddress, pkg_num,
reinterpret_cast<std::atomic<uint64_t> *>(
QueueResource->Queue_write_ptr_aql),
reinterpret_cast<std::atomic<uint64_t> *>(
QueueResource->Queue_read_ptr_aql),
QueueResource->ErrorReason, cmdbuf_size, queue_engine, use_hws);
QueueResource->ErrorReason, cmdbuf_size, queue_engine, use_hws, event_id);

QueueResource->QueueId = reinterpret_cast<HSA_QUEUEID>(queue_);
// for doorbell_signal.hardware_doorbell_ptr
Expand Down
162 changes: 161 additions & 1 deletion projects/rocr-runtime/libhsakmt/src/dxg/wddm/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -874,10 +874,162 @@ bool WDDMDevice::SubmitToSwQueue(WDDMQueue *queue, uint64_t command_addr,
return true;
}

// Compute the CWSR (Context Wave Save/Restore) region size for this device.
//
// Mirrors the Linux KFD calculation in update_ctx_save_restore_size() (queues.c).
// The region must hold, per XCC:
// - HsaUserContextSaveAreaHeader (save-area header)
// - Control stack (wave_num * bytes_per_wave + 8), page-aligned
// - WG data (VGPR + SGPR + LDS + HW-regs per CU), page-aligned
// multiplied by num_xcc for multi-XCC devices.
//
// Constants ported from queues.c / dxg/queues.cpp:
// CNTL_STACK_BYTES_PER_WAVE : gfx10+ = 12, older = 8
// WG_CONTEXT_DATA_SIZE_PER_CU : vgpr_size + SGPR_SIZE_PER_CU + LDS + HWREG_SIZE_PER_CU
// SGPR_SIZE_PER_CU: gfx10+ = 0x5000 (20 KB), older = 0x4000 (16 KB) [matches KMD CalCwsrSaveAreaSize]
// HWREG_SIZE_PER_CU: gfx10+ = 0x1400 (5 KB), older = 0x1000 (4 KB) [matches KMD CalCwsrSaveAreaSize]
// vgpr_size: all gfx10/gfx11/gfx12 = 256KB/CU (0x40000) [matches KMD CalCwsrSaveAreaSize]
// DEBUGGER_BYTES_PER_WAVE = 32, DEBUGGER_BYTES_ALIGN = 64
uint64_t WDDMDevice::AllocateCwsrSize(uint64_t* out_ctx_size, uint64_t* out_debug_size) const {
const uint32_t page_size = 4096;
const uint32_t debugger_bytes_per_wave = 32;
const uint32_t debugger_bytes_align = 64;

const int major = device_info_.major;
const uint32_t num_xcc = device_info_.num_xcc ? device_info_.num_xcc : 1;
// compute_unit_count in device_info_ is the total CU count across all XCCs.
const uint32_t cu_num = device_info_.compute_unit_count / num_xcc;
// wave_per_cu from device_info_ is per-CU (40 for Navi10/12/14, 32 for Navi2x+).
const uint32_t wave_per_cu = device_info_.wave_per_cu ? device_info_.wave_per_cu : 32;
const uint32_t wave_num = cu_num * wave_per_cu;

// KMD CalCwsrSaveAreaSize() split: gfx10+ (FAMILY_NV) vs pre-gfx10
const bool is_gfx10_plus = (major >= 10);

// Control stack: gfx10+ = 12 bytes/wave, older = 8 bytes/wave
const uint32_t bytes_per_wave = is_gfx10_plus ? 12 : 8;
const uint32_t ctl_stack_size = wave_num * bytes_per_wave + 8;

// Sizes from KMD kdx/src/RunListMgr.cpp CalCwsrSaveAreaSize():
// gfx10+: sgpr=0x5000, hwreg=0x1400, vgpr=0x40000
// pre-gfx10: sgpr=0x4000, hwreg=0x1000, vgpr=0x40000
const uint32_t vgpr_size_per_cu = 0x40000;
const uint32_t sgpr_size_per_cu = is_gfx10_plus ? 0x5000 : 0x4000;
const uint32_t hwreg_size_per_cu = is_gfx10_plus ? 0x1400 : 0x1000;
// LDS size stored in device_info_.lds_size (bytes)
const uint32_t lds_size_bytes = device_info_.lds_size;
const uint32_t wg_size_per_cu = vgpr_size_per_cu + sgpr_size_per_cu + lds_size_bytes + hwreg_size_per_cu;
Comment thread
gandryey marked this conversation as resolved.
Resolved
const uint32_t wg_data_size = cu_num * wg_size_per_cu;
Comment thread
gandryey marked this conversation as resolved.
Resolved

// Align sizes to page boundary
auto page_align_up = [&](uint64_t x) -> uint64_t {
return (x + page_size - 1) & ~(uint64_t)(page_size - 1);
};
auto align_up = [&](uint64_t x, uint32_t align) -> uint64_t {
return (x + align - 1) & ~(uint64_t)(align - 1);
};

// ctx_save_restore_size per XCC (header + ctl_stack + wg_data)
uint64_t ctx_size = page_align_up(sizeof(HsaUserContextSaveAreaHeader) + ctl_stack_size)
+ page_align_up(wg_data_size);

// debug_memory_size per XCC
uint64_t debug_size = align_up(wave_num * debugger_bytes_per_wave, debugger_bytes_align);

if (out_ctx_size) *out_ctx_size = ctx_size;
if (out_debug_size) *out_debug_size = debug_size;

uint64_t total = page_align_up((ctx_size + debug_size) * num_xcc);

return total;
}

// Initialize HsaUserContextSaveAreaHeader for each XCC in the CWSR region.
//
// Mirrors Linux's fill_cwsr_header() in queues.c. The CWSR allocation is
// divided into equal-sized per-XCC slots of ctx_save_restore_size bytes, each
// beginning with an HsaUserContextSaveAreaHeader. This must be called after
// the CPU-accessible system memory is allocated so the runtime and debugger can
// locate the control stack, wave state, and debug areas on context save.
//
// Layout per XCC slot (offsets relative to slot base):
// [0] HsaUserContextSaveAreaHeader
// [ctl_stack_offset] Control stack (ctl_stack_size bytes, page-aligned)
// [wg_data_offset] Wave/WG state (wg_data_size bytes, page-aligned)
// [debug_offset] Debugger area (debug_memory_size bytes, 64-byte aligned)
//
// DebugOffset and DebugSize are expressed relative to the start of the *first*
// XCC slot (matching the Linux convention used by the debugger).
Comment thread
gandryey marked this conversation as resolved.
Resolved
void WDDMDevice::FillCwsrHeader(void* cpu_addr, uint64_t ctx_save_restore_size,
uint64_t debug_memory_size, uint32_t num_xcc,
volatile HSAint64* error_reason, HSAuint32 error_event_id) {
for (uint32_t i = 0; i < num_xcc; i++) {
auto* header = reinterpret_cast<HsaUserContextSaveAreaHeader*>(
static_cast<uint8_t*>(cpu_addr) + i * ctx_save_restore_size);

// ErrorEventId: the EventId of the HsaEvent passed at queue creation by the
// runtime (HSA_EVENTTYPE_SIGNAL, shared across all queues on the agent).
// Mirrors Linux fill_cwsr_header(): Event ? Event->EventId : 0.
header->ErrorEventId = error_event_id;

// ErrorReason mirrors Linux fill_cwsr_header(): pointer to the HSA signal
// payload used by the runtime to report the error reason bitmask on
// queue exception. Sourced from QueueResource->ErrorReason, stored on the
// queue as error_reason_ and passed through here.
header->ErrorReason = error_reason;

// DebugOffset is from this XCC's slot base to the debug area of the *last*
// XCC slot, matching fill_cwsr_header():
// header->DebugOffset = (NumXcc - i) * ctx_save_restore_size
header->DebugOffset = static_cast<HSAuint32>((num_xcc - i) * ctx_save_restore_size);
Comment thread
gandryey marked this conversation as resolved.
Resolved
header->DebugSize = static_cast<HSAuint32>(debug_memory_size * num_xcc);

// ControlStackOffset/Size and WaveStateOffset/Size describe where the
// saved control stack and wave state ended up inside this XCC's slot.
// They are written by the kernel (KFD/KMD) during AMDKFD_IOC_GET_QUEUE_WAVE_STATE
// after preemption; see struct kfd_context_save_area_header::wave_state.
// Zero is the correct initial value — no context has been saved yet.
// rocdbgapi reads these fields (queue.cpp) only after a context save has
// occurred, so zeroing here is safe and matches Linux fill_cwsr_header().
header->ControlStackOffset = 0;
header->ControlStackSize = 0;
header->WaveStateOffset = 0;
header->WaveStateSize = 0;
header->Reserved1 = 0;
}
}

bool WDDMDevice::CreateHwQueue(WDDMQueue *queue) {
void *priv_data;
int priv_size;

// Allocate CWSR (Context Wave Save/Restore) region in system (GTT) memory.
// Matches Linux: anonymous mmap + register_svm_range(alwaysMapped=true).
// locked keeps pages pinned (HSA_SVM_FLAG_GPU_ALWAYS_MAPPED equivalent).
// The allocation handle is passed to KMD via UMDKMDIF_CREATEHWQUEUE_PRIVATE_DATA::CwsrMemHandle.
if (queue->cwsr_mem_ == nullptr) {
Comment thread
gandryey marked this conversation as resolved.
Resolved
Comment thread
gandryey marked this conversation as resolved.
Resolved
uint64_t ctx_save_restore_size = 0;
uint64_t debug_memory_size = 0;
GpuMemoryCreateInfo cwsr_create_info{};
cwsr_create_info.domain = Wkmi::kSystem;
cwsr_create_info.size = AllocateCwsrSize(&ctx_save_restore_size, &debug_memory_size);

GpuMemory *cwsr_gpu_mem = nullptr;
ErrorCode cwsr_code = CreateGpuMemory(cwsr_create_info, &cwsr_gpu_mem);
if (cwsr_code != ErrorCode::Success) {
pr_err("CWSR memory allocation failed\n");
return false;
}
queue->cwsr_mem_ = cwsr_gpu_mem->GetGpuMemoryHandle();
queue->cwsr_mem_handle_ = cwsr_gpu_mem->KmtHandle();

// Initialise the per-XCC HsaUserContextSaveAreaHeader in the CPU-visible
// system memory, mirroring fill_cwsr_header() in Linux queues.c.
const uint32_t num_xcc = device_info_.num_xcc ? device_info_.num_xcc : 1;
FillCwsrHeader(cwsr_gpu_mem->CpuAddress(), ctx_save_restore_size,
debug_memory_size, num_xcc, queue->error_reason_, queue->error_event_id_);
}

priv_size = Wkmi::GetHwQueuePrivDataSize();
priv_data = malloc(priv_size);
assert(priv_data);
Expand All @@ -897,7 +1049,8 @@ bool WDDMDevice::CreateHwQueue(WDDMQueue *queue) {
}
Wkmi::FillinHwQueuePrivData(priv_data, FwManagedGfxState, queue->prio, is_aql,
queue->cmdbuf_addr, queue->cmdbuf_size, reinterpret_cast<uintptr_t>(queue->ring_wptr),
reinterpret_cast<uintptr_t>(queue->ring_rptr), resource, &doorbell_loc);
reinterpret_cast<uintptr_t>(queue->ring_rptr), resource, &doorbell_loc,
queue->cwsr_mem_handle_);

D3DKMT_CREATEHWQUEUE createHwQueue = {0};
createHwQueue.hHwContext = queue->context;
Expand Down Expand Up @@ -935,6 +1088,13 @@ bool WDDMDevice::DestroyHwQueue(WDDMQueue *queue) {
return false;
}

if (queue->cwsr_mem_ != nullptr) {
Comment thread
gandryey marked this conversation as resolved.
Resolved
auto cwsr_gpu_mem = GpuMemory::Convert(queue->cwsr_mem_);
delete cwsr_gpu_mem;
queue->cwsr_mem_ = nullptr;
queue->cwsr_mem_handle_ = 0;
}

return true;
}

Expand Down
4 changes: 3 additions & 1 deletion projects/rocr-runtime/libhsakmt/src/dxg/wddm/queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ void ComputeQueue::AqlToPm4Thread(ComputeQueue* queue) {
ComputeQueue::ComputeQueue(WDDMDevice* device, void* ring, uint64_t ring_size,
std::atomic<uint64_t>* _ring_wptr, std::atomic<uint64_t>* _ring_rptr,
volatile int64_t* error_addr, uint32_t cmdbuf_size, uint32_t engine,
bool use_hws)
bool use_hws, HSAuint32 event_id)
: WDDMQueue(
device, (use_hws && device->IsAqlSupported()) ? reinterpret_cast<uintptr_t>(ring) : 0,
(use_hws && device->IsAqlSupported()) ? ring_size * 64 : cmdbuf_size, engine, use_hws),
Expand All @@ -271,6 +271,8 @@ ComputeQueue::ComputeQueue(WDDMDevice* device, void* ring, uint64_t ring_size,
scratch_base_(nullptr) {
ring_wptr = _ring_wptr;
ring_rptr = _ring_rptr;
error_reason_ = error_addr;
error_event_id_ = event_id;
amd_queue_rocr_ = (amd_queue_v2_t*)((char*)ring_rptr - offsetof(amd_queue_t, read_dispatch_id));
amd_queue_memory_ = GetGpuMemoryFromAddress(amd_queue_rocr_);
native_aql_ = use_hws && device->IsAqlSupported();
Expand Down
Loading