wsl/hsakmt: initial commit

Signed-off-by: lyndonli <Lyndon.Li@amd.com>
Signed-off-by: Horatio Zhang <Hongkun.Zhang@amd.com>
Signed-off-by: Shi.Leslie <Yuliang.Shi@amd.com>
Signed-off-by: LonglongYao <Longlong.Yao@amd.com>
Signed-off-by: tiancyin <tianci.yin@amd.com>
Signed-off-by: Frank Min <Frank.Min@amd.com>
Signed-off-by: Aaron Liu <aaron.liu@amd.com>
Signed-off-by: Shane Xiao <shane.xiao@amd.com>
Signed-off-by: Lang Yu <lang.yu@amd.com>
Signed-off-by: Feifei Xu <Feifei.Xu@amd.com>
Signed-off-by: Ruili Ji <ruiliji2@amd.com>
Signed-off-by: Qiang Yu <qiang.yu@amd.com>
Signed-off-by: Flora Cui <flora.cui@amd.com>
Šī revīzija ir iekļauta:
Flora Cui
2024-04-30 09:01:09 -05:00
revīziju iesūtīja Frank Min
revīzija 79a63cf292
48 mainīti faili ar 13833 papildinājumiem un 0 dzēšanām
+118
Parādīt failu
@@ -0,0 +1,118 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include <cassert>
#include <cstring>
#include "libhsakmt.h"
static uint32_t runtime_capabilities_mask = 0;
HSAKMT_STATUS HSAKMTAPI hsaKmtDbgRegister(HSAuint32 NodeId) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDbgUnregister(HSAuint32 NodeId) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDbgWavefrontControl(
HSAuint32 NodeId, HSA_DBG_WAVEOP Operand, HSA_DBG_WAVEMODE Mode,
HSAuint32 TrapId, HsaDbgWaveMessage *DbgWaveMsgRing) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDbgAddressWatch(
HSAuint32 NodeId, HSAuint32 NumWatchPoints, HSA_DBG_WATCH_MODE WatchMode[],
void *WatchAddress[], HSAuint64 WatchMask[], HsaEvent *WatchEvent[]) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtCheckRuntimeDebugSupport(void) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtRuntimeEnable(void *rDebug, bool setupTtmp) {
HSAKMT_STATUS result = hsaKmtCheckRuntimeDebugSupport();
if (result)
return result;
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtRuntimeDisable(void) {
HSAKMT_STATUS result = hsaKmtCheckRuntimeDebugSupport();
if (result)
return result;
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtGetRuntimeCapabilities(HSAuint32 *caps_mask) {
CHECK_DXG_OPEN();
*caps_mask = runtime_capabilities_mask;
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDbgEnable(void **runtime_info,
HSAuint32 *data_size) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDbgDisable(void) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDbgGetDeviceData(void **data,
HSAuint32 *n_entries,
HSAuint32 *entry_size) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDbgGetQueueData(void **data, HSAuint32 *n_entries,
HSAuint32 *entry_size,
bool suspend_queues) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI
hsaKmtDebugTrapIoctl(struct kfd_ioctl_dbg_trap_args *args, HSA_QUEUEID *Queues,
HSAuint64 *DebugReturn) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
+129
Parādīt failu
@@ -0,0 +1,129 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include <cstdio>
#include <cassert>
#include <thread>
#include <chrono>
#include "libhsakmt.h"
HSAKMT_STATUS HSAKMTAPI hsaKmtCreateEvent(HsaEventDescriptor *EventDesc,
bool ManualReset, bool IsSignaled,
HsaEvent **Event) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDestroyEvent(HsaEvent *Event) {
CHECK_DXG_OPEN();
if (!Event)
return HSAKMT_STATUS_INVALID_HANDLE;
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtSetEvent(HsaEvent *Event) {
CHECK_DXG_OPEN();
if (!Event)
return HSAKMT_STATUS_INVALID_HANDLE;
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtResetEvent(HsaEvent *Event) {
CHECK_DXG_OPEN();
if (!Event)
return HSAKMT_STATUS_INVALID_HANDLE;
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtQueryEventState(HsaEvent *Event) {
CHECK_DXG_OPEN();
if (!Event)
return HSAKMT_STATUS_INVALID_HANDLE;
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtWaitOnEvent(HsaEvent *Event,
HSAuint32 Milliseconds) {
return hsaKmtWaitOnEvent_Ext(Event, Milliseconds, NULL);
}
HSAKMT_STATUS HSAKMTAPI hsaKmtWaitOnEvent_Ext(HsaEvent *Event,
HSAuint32 Milliseconds,
uint64_t *event_age) {
if (!Event)
return HSAKMT_STATUS_INVALID_HANDLE;
return hsaKmtWaitOnMultipleEvents_Ext(&Event, 1, true, Milliseconds,
event_age);
}
HSAKMT_STATUS HSAKMTAPI hsaKmtWaitOnMultipleEvents(HsaEvent *Events[],
HSAuint32 NumEvents,
bool WaitOnAll,
HSAuint32 Milliseconds) {
return hsaKmtWaitOnMultipleEvents_Ext(Events, NumEvents, WaitOnAll,
Milliseconds, NULL);
}
HSAKMT_STATUS HSAKMTAPI hsaKmtWaitOnMultipleEvents_Ext(HsaEvent *Events[],
HSAuint32 NumEvents,
bool WaitOnAll,
HSAuint32 Milliseconds,
uint64_t *event_age) {
CHECK_DXG_OPEN();
if (!Events)
return HSAKMT_STATUS_INVALID_HANDLE;
if (NumEvents == 1 && Events[0] == nullptr) {
std::this_thread::sleep_for(std::chrono::microseconds(20));
return HSAKMT_STATUS_SUCCESS;
}
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtOpenSMI(HSAuint32 NodeId, int *fd) {
CHECK_DXG_OPEN();
pr_debug("[%s] node %d\n", __func__, NodeId);
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
+47
Parādīt failu
@@ -0,0 +1,47 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include "libhsakmt.h"
// HSAKMT global data
int kfd_fd = -1;
unsigned long dxg_open_count;
unsigned long system_properties_count;
pthread_mutex_t hsakmt_mutex = PTHREAD_MUTEX_INITIALIZER;
bool is_dgpu;
#ifndef PAGE_SIZE
int PAGE_SIZE;
#endif
int PAGE_SHIFT;
/* whether to check all dGPUs in the topology support SVM API */
bool is_svm_api_supported;
/* zfb is mainly used during emulation */
int zfb_support;
/* handle vendor specific packet */
int vendor_packet_support;
+1064
Parādīt failu
Failā izmaiņas netiks attēlotas, jo tās ir par lielu Ielādēt izmaiņas
+363
Parādīt failu
@@ -0,0 +1,363 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
// This file is used only for open source cmake builds, if we hardcode the
// register values in amd_aql_queue.cpp then this file won't be required. For
// now we are using this file where register details are spelled out in the
// structs/unions below.
#ifndef HSA_RUNTME_CORE_INC_REGISTERS_H_
#define HSA_RUNTME_CORE_INC_REGISTERS_H_
typedef enum SQ_RSRC_BUF_TYPE {
SQ_RSRC_BUF = 0x00000000,
SQ_RSRC_BUF_RSVD_1 = 0x00000001,
SQ_RSRC_BUF_RSVD_2 = 0x00000002,
SQ_RSRC_BUF_RSVD_3 = 0x00000003,
} SQ_RSRC_BUF_TYPE;
typedef enum BUF_DATA_FORMAT {
BUF_DATA_FORMAT_INVALID = 0x00000000,
BUF_DATA_FORMAT_8 = 0x00000001,
BUF_DATA_FORMAT_16 = 0x00000002,
BUF_DATA_FORMAT_8_8 = 0x00000003,
BUF_DATA_FORMAT_32 = 0x00000004,
BUF_DATA_FORMAT_16_16 = 0x00000005,
BUF_DATA_FORMAT_10_11_11 = 0x00000006,
BUF_DATA_FORMAT_11_11_10 = 0x00000007,
BUF_DATA_FORMAT_10_10_10_2 = 0x00000008,
BUF_DATA_FORMAT_2_10_10_10 = 0x00000009,
BUF_DATA_FORMAT_8_8_8_8 = 0x0000000a,
BUF_DATA_FORMAT_32_32 = 0x0000000b,
BUF_DATA_FORMAT_16_16_16_16 = 0x0000000c,
BUF_DATA_FORMAT_32_32_32 = 0x0000000d,
BUF_DATA_FORMAT_32_32_32_32 = 0x0000000e,
BUF_DATA_FORMAT_RESERVED_15 = 0x0000000f,
} BUF_DATA_FORMAT;
typedef enum BUF_NUM_FORMAT {
BUF_NUM_FORMAT_UNORM = 0x00000000,
BUF_NUM_FORMAT_SNORM = 0x00000001,
BUF_NUM_FORMAT_USCALED = 0x00000002,
BUF_NUM_FORMAT_SSCALED = 0x00000003,
BUF_NUM_FORMAT_UINT = 0x00000004,
BUF_NUM_FORMAT_SINT = 0x00000005,
BUF_NUM_FORMAT_SNORM_OGL__SI__CI = 0x00000006,
BUF_NUM_FORMAT_RESERVED_6__VI = 0x00000006,
BUF_NUM_FORMAT_FLOAT = 0x00000007,
} BUF_NUM_FORMAT;
typedef enum BUF_FORMAT {
BUF_FORMAT_32_UINT = 0x00000014,
} BUF_FORMAT;
typedef enum SQ_SEL_XYZW01 {
SQ_SEL_0 = 0x00000000,
SQ_SEL_1 = 0x00000001,
SQ_SEL_RESERVED_0 = 0x00000002,
SQ_SEL_RESERVED_1 = 0x00000003,
SQ_SEL_X = 0x00000004,
SQ_SEL_Y = 0x00000005,
SQ_SEL_Z = 0x00000006,
SQ_SEL_W = 0x00000007,
} SQ_SEL_XYZW01;
union COMPUTE_TMPRING_SIZE {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int WAVES : 12;
unsigned int WAVESIZE : 13;
unsigned int : 7;
#elif defined(BIGENDIAN_CPU)
unsigned int : 7;
unsigned int WAVESIZE : 13;
unsigned int WAVES : 12;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
union COMPUTE_TMPRING_SIZE_GFX11 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int WAVES : 12;
unsigned int WAVESIZE : 15;
unsigned int : 5;
#elif defined(BIGENDIAN_CPU)
unsigned int : 5;
unsigned int WAVESIZE : 15;
unsigned int WAVES : 12;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
union COMPUTE_TMPRING_SIZE_GFX12 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int WAVES : 12;
unsigned int WAVESIZE : 18;
unsigned int : 2;
#elif defined(BIGENDIAN_CPU)
unsigned int : 2;
unsigned int WAVESIZE : 18;
unsigned int WAVES : 12;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
union SQ_BUF_RSRC_WORD0 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int BASE_ADDRESS : 32;
#elif defined(BIGENDIAN_CPU)
unsigned int BASE_ADDRESS : 32;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
union SQ_BUF_RSRC_WORD1 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int BASE_ADDRESS_HI : 16;
unsigned int STRIDE : 14;
unsigned int CACHE_SWIZZLE : 1;
unsigned int SWIZZLE_ENABLE : 1;
#elif defined(BIGENDIAN_CPU)
unsigned int SWIZZLE_ENABLE : 1;
unsigned int CACHE_SWIZZLE : 1;
unsigned int STRIDE : 14;
unsigned int BASE_ADDRESS_HI : 16;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
union SQ_BUF_RSRC_WORD1_GFX11 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int BASE_ADDRESS_HI : 16;
unsigned int STRIDE : 14;
unsigned int SWIZZLE_ENABLE : 2;
#elif defined(BIGENDIAN_CPU)
unsigned int SWIZZLE_ENABLE : 2;
unsigned int STRIDE : 14;
unsigned int BASE_ADDRESS_HI : 16;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
union SQ_BUF_RSRC_WORD2 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int NUM_RECORDS : 32;
#elif defined(BIGENDIAN_CPU)
unsigned int NUM_RECORDS : 32;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
union SQ_BUF_RSRC_WORD3 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int DST_SEL_X : 3;
unsigned int DST_SEL_Y : 3;
unsigned int DST_SEL_Z : 3;
unsigned int DST_SEL_W : 3;
unsigned int NUM_FORMAT : 3;
unsigned int DATA_FORMAT : 4;
unsigned int ELEMENT_SIZE : 2;
unsigned int INDEX_STRIDE : 2;
unsigned int ADD_TID_ENABLE : 1;
unsigned int ATC__CI__VI : 1;
unsigned int HASH_ENABLE : 1;
unsigned int HEAP : 1;
unsigned int MTYPE__CI__VI : 3;
unsigned int TYPE : 2;
#elif defined(BIGENDIAN_CPU)
unsigned int TYPE : 2;
unsigned int MTYPE__CI__VI : 3;
unsigned int HEAP : 1;
unsigned int HASH_ENABLE : 1;
unsigned int ATC__CI__VI : 1;
unsigned int ADD_TID_ENABLE : 1;
unsigned int INDEX_STRIDE : 2;
unsigned int ELEMENT_SIZE : 2;
unsigned int DATA_FORMAT : 4;
unsigned int NUM_FORMAT : 3;
unsigned int DST_SEL_W : 3;
unsigned int DST_SEL_Z : 3;
unsigned int DST_SEL_Y : 3;
unsigned int DST_SEL_X : 3;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
union SQ_BUF_RSRC_WORD3_GFX10 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int DST_SEL_X : 3;
unsigned int DST_SEL_Y : 3;
unsigned int DST_SEL_Z : 3;
unsigned int DST_SEL_W : 3;
unsigned int FORMAT : 7;
unsigned int RESERVED1 : 2;
unsigned int INDEX_STRIDE : 2;
unsigned int ADD_TID_ENABLE : 1;
unsigned int RESOURCE_LEVEL : 1;
unsigned int RESERVED2 : 3;
unsigned int OOB_SELECT : 2;
unsigned int TYPE : 2;
#elif defined(BIGENDIAN_CPU)
unsigned int TYPE : 2;
unsigned int OOB_SELECT : 2;
unsigned int RESERVED2 : 3;
unsigned int RESOURCE_LEVEL : 1;
unsigned int ADD_TID_ENABLE : 1;
unsigned int INDEX_STRIDE : 2;
unsigned int RESERVED1 : 2;
unsigned int FORMAT : 7;
unsigned int DST_SEL_W : 3;
unsigned int DST_SEL_Z : 3;
unsigned int DST_SEL_Y : 3;
unsigned int DST_SEL_X : 3;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
// From V# Table
union SQ_BUF_RSRC_WORD3_GFX11 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int DST_SEL_X : 3;
unsigned int DST_SEL_Y : 3;
unsigned int DST_SEL_Z : 3;
unsigned int DST_SEL_W : 3;
unsigned int FORMAT : 6;
unsigned int RESERVED1 : 3;
unsigned int INDEX_STRIDE : 2;
unsigned int ADD_TID_ENABLE : 1;
unsigned int RESERVED2 : 4;
unsigned int OOB_SELECT : 2;
unsigned int TYPE : 2;
#elif defined(BIGENDIAN_CPU)
unsigned int TYPE : 2;
unsigned int OOB_SELECT : 2;
unsigned int RESERVED2 : 4;
unsigned int ADD_TID_ENABLE : 1;
unsigned int INDEX_STRIDE : 2;
unsigned int RESERVED1 : 3;
unsigned int FORMAT : 6;
unsigned int DST_SEL_W : 3;
unsigned int DST_SEL_Z : 3;
unsigned int DST_SEL_Y : 3;
unsigned int DST_SEL_X : 3;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
// From V# Table
union SQ_BUF_RSRC_WORD3_GFX12 {
struct {
#if defined(LITTLEENDIAN_CPU)
unsigned int DST_SEL_X : 3;
unsigned int DST_SEL_Y : 3;
unsigned int DST_SEL_Z : 3;
unsigned int DST_SEL_W : 3;
unsigned int FORMAT : 6;
unsigned int RESERVED1 : 3;
unsigned int INDEX_STRIDE : 2;
unsigned int ADD_TID_ENABLE : 1;
unsigned int WRITE_COMPRESS_ENABLE : 1;
unsigned int COMPRESSION_EN : 1;
unsigned int COMPRESSION_ACCESS_MODE : 2;
unsigned int OOB_SELECT : 2;
unsigned int TYPE : 2;
#elif defined(BIGENDIAN_CPU)
unsigned int TYPE : 2;
unsigned int OOB_SELECT : 2;
unsigned int COMPRESSION_ACCESS_MODE : 2;
unsigned int COMPRESSION_EN : 1;
unsigned int WRITE_COMPRESS_ENABLE : 1;
unsigned int ADD_TID_ENABLE : 1;
unsigned int INDEX_STRIDE : 2;
unsigned int RESERVED1 : 3;
unsigned int FORMAT : 6;
unsigned int DST_SEL_W : 3;
unsigned int DST_SEL_Z : 3;
unsigned int DST_SEL_Y : 3;
unsigned int DST_SEL_X : 3;
#endif
} bitfields, bits;
unsigned int u32All;
signed int i32All;
float f32All;
};
#endif // header guard
+131
Parādīt failu
@@ -0,0 +1,131 @@
#ifndef _ROCR_PROXY_H_
#define _ROCR_PROXY_H_
#include <vector>
namespace rocr_proxy {
enum AllocDomain {
kSystem,
kLocal,
kUserMemory,
kUserQueue,
kDomainCount,
};
enum MemFlag {
kFineGrain = (1ULL << 0),
kKernarg = (1ULL << 1),
};
enum EngineFlag {
KCOMPUTE0 = (1ULL << 0),
KDRMDMA = (1ULL << 1),
KDRMDMA1 = (1ULL << 2),
};
enum SchedLevel {
kLow = 0,
kNormal = 1,
kHigh = 2,
};
enum AsicFamilyType {
kVega10,
kNavi10,
kSiennaCichlid,
kPlumBONITO,
kNavi44,
kNavi48
};
struct HwsInfo {
union {
struct {
uint32_t gfxHwsEnabled : 1;
uint32_t computeHwsEnabled : 1;
uint32_t dmaHwsEnabled : 1;
uint32_t dma1HwsEnabled : 1;
uint32_t reserved : 28;
} hwsMask;
uint32_t osHwsEnableFlags;
};
uint64_t engineOrdinalMask; // Indicates which engines (by ordinal) support MES HWS
};
typedef struct {
int major;
int minor;
int stepping;
bool is_dgpu;
char product_name[MAX_PATH];
const char *uuid;
AsicFamilyType family;
uint32_t device_id;
uint32_t wavefront_size;
uint32_t compute_unit_count;
uint32_t max_engine_clock_mhz;
uint32_t watch_points_num;
uint32_t pci_bus_addr;
uint32_t memory_bus_width;
uint32_t max_memory_clock_mhz;
uint64_t gpu_counter_frequency;
uint32_t wave_per_cu;
uint32_t simd_per_cu;
uint32_t max_scratch_slots_per_cu;
uint32_t num_shader_engine;
uint32_t shader_array_per_shader_engine;
uint32_t domain;
uint32_t num_gws;
uint32_t asic_revision;
uint64_t local_visible_heap_size;
uint64_t local_invisible_heap_size;
uint64_t private_aperture_base;
uint64_t private_aperture_size;
uint64_t shared_aperture_base;
uint64_t shared_aperture_size;
uint32_t user_queue_size;
uint32_t lds_size;
uint32_t big_page_alignment_size;
uint32_t hw_big_page_min_alignment_size;
uint32_t hw_big_page_alignment_size;
bool enable_big_page_alignment;
uint32_t mec_fw_version;
uint32_t sdma_fw_version;
uint32_t l1_cache_size;
uint32_t l2_cache_size;
uint32_t l3_cache_size;
uint32_t gl2_cacheline_size;
uint32_t num_cp_queues;
HwsInfo hwsInfo;
std::vector<int> sdma_schedid;
uint32_t compute_schedid;
bool state_shadowing_by_cpfw;
bool platform_atomic_support;
void *adapter_info;
void *adapter_ex_info;
} DeviceInfo;
int EngineOrdinal(int engine, DeviceInfo *device_info);
bool GetHwsEnabled(int engine, DeviceInfo *device_info);
bool ShouldDisableGpuTimeout(int engine, DeviceInfo *device_info);
bool ParseAdapterInfo(D3DKMT_HANDLE adapter, DeviceInfo *device_info);
bool QueryAdapterSupported(D3DKMT_HANDLE adapter);
uint32_t QueueEngine2EngineFlag(uint32_t queue_engine);
void SetAllocationInfo(void *data, uint64_t size, AllocDomain domain,
uint64_t addr, uint32_t mem_flags, uint32_t engine_flag, const DeviceInfo &device_info);
bool CreatePrivateAllocInfo(int num_handles, void **ppdrv_priv, void **ppalloc_priv,
int *pdrv_priv_data_size, int *palloc_priv_data_size);
void DestroyPrivateAllocInfo(void *drv_priv, void *alloc_priv);
int CreateSubmitPrivData(void **priv_data, D3DKMT_HANDLE queue, uint64_t command_addr,
uint64_t command_size, bool is_hw_queue);
int CreateHwQueuePrivData(void **priv_data, D3DKMT_HANDLE context,
bool FwManagedGfxState, SchedLevel level = kNormal);
int CreateContextPrivData(void **priv_data, bool FwManagedGfxState);
int CreatePowerOptPrivData(void **priv_data, bool restore);
int CreateCalibratedTimestampsPrivData(void **priv_data);
void QueryCalibratedTimestamps(void* priv, uint64_t* gpu, uint64_t* cpu);
void DestroyPrivData(void *priv_data);
}
#endif
+155
Parādīt failu
@@ -0,0 +1,155 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2015, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef _ROCR_WDDM_TYPES_H_
#define _ROCR_WDDM_TYPES_H_
#include <stdint.h>
#include <no_sal2.h>
typedef uint32_t UINT, *UINT_PTR;
typedef int32_t INT32;
typedef int32_t LONG;
typedef uint32_t ULONG, *ULONG_PTR;
typedef int64_t LONGLONG;
typedef int64_t LONG64;
typedef uint64_t ULONGLONG;
typedef uint64_t ULONG64, *ULONG64_PTR;
typedef uint8_t BYTE;
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef int32_t BOOL;
typedef int32_t NTSTATUS;
typedef uint16_t USHORT;
typedef uint16_t UINT16;
typedef uint32_t UINT32;
typedef uint64_t UINT64;
typedef int32_t INT;
typedef uint64_t SIZE_T;
typedef void VOID;
typedef float FLOAT;
typedef char CHAR;
typedef unsigned char UCHAR;
typedef UCHAR BOOLEAN;
typedef int16_t WCHAR;
typedef void *HANDLE;
typedef void *PVOID;
typedef void *LPVOID;
typedef const int16_t *PCWSTR;
#define ULONG ULONG
#define ULONG_PTR ULONG_PTR
#define USHORT USHORT
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HDC);
DECLARE_HANDLE(PALETTEENTRY);
typedef struct tagPOINT {
LONG x;
LONG y;
} POINT;
typedef struct tagRECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT;
typedef struct tagRECTL {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECTL;
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
DWORD HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER;
typedef LARGE_INTEGER *PLARGE_INTEGER;
typedef struct _LUID {
ULONG LowPart;
LONG HighPart;
} LUID, *PLUID;
typedef enum _DEVICE_POWER_STATE {
PowerDeviceUnspecified = 0,
PowerDeviceD0,
PowerDeviceD1,
PowerDeviceD2,
PowerDeviceD3,
PowerDeviceMaximum
} DEVICE_POWER_STATE, *PDEVICE_POWER_STATE;
#define _Check_return_
#define APIENTRY
#define CONST const
#define IN
#define OUT
#define FAR
#define MAX_PATH 260
#define __stdcall
#ifndef GUID_DEFINED
#define GUID_DEFINED
typedef struct _GUID {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[ 8 ];
} GUID;
#endif
#include <guiddef.h>
#endif
+77
Parādīt failu
@@ -0,0 +1,77 @@
/* Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. */
#ifndef _CMD_UTIL_H_
#define _CMD_UTIL_H_
#include <string.h>
#include "hsa-runtime/inc/hsa.h"
#include "hsa-runtime/inc/amd_hsa_queue.h"
#include "hsa-runtime/inc/amd_hsa_kernel_code.h"
#include "inc/pm4_cmds.h"
#include "util/utils.h"
namespace rocr {
namespace core {
struct DispatchInfo {
uint8_t major;
hsa_kernel_dispatch_packet_t *pPacket;
void *pEntry;
const amd_kernel_code_t *pKernelObject;
uint32_t ldsBlks;
amd_queue_t *pAmdQueue;
bool wave32;
uint32_t srd;
void *pScratchBase;
uint32_t scratchSizePerWave;
uint32_t scratchBaseOffset[2];
uint32_t offsetCnt;
};
class CmdUtil {
public:
CmdUtil() {};
~CmdUtil() {};
size_t BuildCopyData(
uint64_t *pDstAddr,
void *pBuffer,
uint32_t dstSel = dst_sel__mec_copy_data__tc_l2,
uint32_t dstCachePolicy = dst_cache_policy__mec_copy_data__stream,
uint32_t srcSel = src_sel__mec_copy_data__gpu_clock_count,
uint32_t srcCachePolicy = src_cache_policy__mec_copy_data__lru,
uint32_t countSel = count_sel__mec_copy_data__64_bits_of_data,
uint32_t wrConfirm = wr_confirm__mec_copy_data__wait_for_confirmation);
size_t BuildBarrier(
void *pBuffer,
uint32_t eventIndex = event_index__mec_event_write__cs_partial_flush,
uint32_t eventType = CS_PARTIAL_FLUSH);
size_t BuildAcquireMem(
uint8_t major,
void *pBuffer);
size_t BuildScratch(
void *pScratchBase,
void *pBuffer);
size_t BuildComputeShaderParams(
void *pBuffer);
size_t BuildDispatch(
struct DispatchInfo *pInfo,
void *pBuffer);
size_t BuildAtomicMem(
uint64_t *pAddr,
uint32_t atomic,
void *pBuffer,
uint32_t cachePolicy = cache_policy__mec_atomic_mem__stream,
uint64_t srcData = 1);
};
} // namespace core
} // namespace rocr
#endif
+245
Parādīt failu
@@ -0,0 +1,245 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef _ROCR_WDDM_H_
#define _ROCR_WDDM_H_
#include <cassert>
#include <ntstatus.h>
#include <atomic>
#include <memory>
#include <vector>
#include "inc/wddm/types.h"
#include "inc/rocr_proxy/rocr_proxy.h"
#include "inc/wddm/va_mgr.h"
#include "inc/wddm/status.h"
#include "inc/wddm/types.h"
#include "inc/wddm/gpu_memory.h"
#include "inc/wddm/cmd_util.h"
namespace rocr {
namespace core {
//class Queue;
class WDDMQueue;
// WSL2 hyperv GPADL protocol limitation
#define MAX_USERPTR_BLOCK_SIZE 0xf0000000
class WDDMDevice {
public:
static constexpr size_t GpuMemoryChunkSize = 2 * (1ULL << 30); // 2 GB
WDDMDevice(D3DKMT_HANDLE adapter, LUID adapter_luid);
~WDDMDevice();
int Major() { return device_info_.major; }
int Minor() { return device_info_.minor; }
int Stepping() { return device_info_.stepping; }
bool IsDgpu() { return device_info_.is_dgpu; }
const char *ProductName() { return device_info_.product_name; }
const char *Uuid() { return device_info_.uuid; }
rocr_proxy::AsicFamilyType GfxFamily() { return device_info_.family; }
uint32_t DeviceId() { return device_info_.device_id; }
uint32_t WavefrontSize() { return device_info_.wavefront_size; }
uint32_t ComputeUnitCount() { return device_info_.compute_unit_count; }
uint32_t MaxEngineClockMhz() { return device_info_.max_engine_clock_mhz; }
uint32_t WatchPointsNum() { return device_info_.watch_points_num; }
uint32_t PciBusAddr() { return device_info_.pci_bus_addr; }
uint32_t MemoryBusWidth() { return device_info_.memory_bus_width; }
uint32_t MaxMemoryClockMhz() { return device_info_.max_memory_clock_mhz; }
uint32_t WavePerCu() { return device_info_.wave_per_cu; }
uint32_t SimdPerCu() { return device_info_.simd_per_cu; }
uint32_t MaxScratchSlotsPerCu() { return device_info_.max_scratch_slots_per_cu; }
uint32_t NumShaderEngine() { return device_info_.num_shader_engine; }
uint32_t ShaderArrayPerShaderEngine() { return device_info_.shader_array_per_shader_engine; }
uint32_t NumSdmaEngine() { return device_info_.sdma_schedid.size(); }
uint32_t Domain() { return device_info_.domain; }
uint32_t NumGws() { return device_info_.num_gws; }
uint32_t AsicRevision() { return device_info_.asic_revision; }
uint64_t LocalHeapSize() { return device_info_.local_visible_heap_size + device_info_.local_invisible_heap_size; }
uint64_t LocalVisibleHeapSize() { return device_info_.local_visible_heap_size; }
uint64_t LocalInvisibleHeapSize() { return device_info_.local_invisible_heap_size; }
uint64_t PrivateApertureBase() { return device_info_.private_aperture_base; }
uint64_t PrivateApertureSize() { return device_info_.private_aperture_size; }
uint64_t SharedApertureBase() { return device_info_.shared_aperture_base; }
uint64_t SharedApertureSize() { return device_info_.shared_aperture_size; }
uint32_t LdsSize() { return device_info_.lds_size; }
uint64_t GPUCounterFrequency() { return device_info_.gpu_counter_frequency; }
uint32_t GetSwsQueueSize(void) const { return device_info_.user_queue_size; }
uint32_t GetMecFwVersion() { return device_info_.mec_fw_version; }
uint32_t GetSdmaFwVersion() { return device_info_.sdma_fw_version; }
uint32_t GetL1CacheSize() { return device_info_.l1_cache_size; }
uint32_t GetL2CacheSize() { return device_info_.l2_cache_size; }
uint32_t GetL3CacheSize() { return device_info_.l3_cache_size; }
uint32_t Gl2CacheLineSize() { return device_info_.gl2_cacheline_size; }
bool SupportStateShadowingByCpFw(void) const { return device_info_.state_shadowing_by_cpfw; }
bool SupportPlatformAtomic(void) const { return device_info_.platform_atomic_support; }
uint32_t GetSdmaEngine(uint32_t idx) {
assert(idx < NumSdmaEngine());
return device_info_.sdma_schedid[idx];
}
uint32_t GetComputeEngine() { return device_info_.compute_schedid; }
uint64_t VramAvail();
void GetClockCounters(uint64_t *gpu, uint64_t *cpu);
uint32_t GetNumCpQueues() { return device_info_.num_cp_queues; }
bool CreateSyncobj(D3DKMT_HANDLE *handle, uint64_t **addr);
void DestroySyncobj(D3DKMT_HANDLE handle);
bool CreateQueue(WDDMQueue *queue);
void DestroyQueue(WDDMQueue *queue);
bool CreateHwQueue(WDDMQueue *queue);
bool DestroyHwQueue(WDDMQueue *queue);
bool SubmitToSwQueue(WDDMQueue *queue, uint64_t command_addr,
uint64_t command_size, uint64_t fence_value);
bool SubmitToHwQueue(WDDMQueue *queue, uint64_t command_addr,
uint64_t command_size, uint64_t fence_value);
bool WaitPagingFence(WDDMQueue *queue) {
uint64_t value = page_fence_value_;
if (*page_fence_addr_ < value &&
!GpuWait(queue, &page_syncobj_, &value, 1))
return false;
return true;
}
bool GpuWait(WDDMQueue *queue, const D3DKMT_HANDLE *syncobjs,
uint64_t *values, int count);
bool GpuSignal(D3DKMT_HANDLE context, const D3DKMT_HANDLE *syncobjs,
uint64_t *value, int count);
bool CpuWait(const D3DKMT_HANDLE *syncobjs, uint64_t *value,
int count, bool wait_any);
bool WaitOnPagingFenceFromCpu();
uint32_t LdsBlocks(const hsa_kernel_dispatch_packet_t *pkt);
uint32_t GetCmdbufSize(void) const { return cmdbuf_size_; }
uint32_t GetAqlFrameSize(void) const { return cmdbuf_aql_frame_size_; }
static uint32_t GetAqlFrameNum(void) { return cmdbuf_aql_frame_num_; }
// Both legacy HWS and stage 1 HWS use KMD to alloc use queue memory,
// return false by default
bool AllocUserQueueMemFromUMD(void) const { return false; }
bool IsHwsEnabled(int engine) {
return rocr_proxy::GetHwsEnabled(engine, &device_info_);
}
void UpdatePageFence(uint64_t fence_value);
D3DKMT_HANDLE PagingQueue() const { return page_queue_; }
D3DKMT_HANDLE PagingFence() const { return page_syncobj_; }
D3DKMT_HANDLE DeviceHandle() const { return device_; }
LUID GetLuid() const { return adapter_luid_; }
const rocr_proxy::DeviceInfo& DeviceInfo() const { return device_info_; }
ErrorCode ReserveGpuVirtualAddress(rocr_proxy::AllocDomain domain,
gpusize hit_base_addr,
gpusize size,
gpusize *out_gpu_virtual_addr,
gpusize alignment,
bool lock=false);
ErrorCode FreeGpuVirtualAddress(rocr_proxy::AllocDomain domain,
gpusize base_addr,
gpusize size);
ErrorCode CreateGpuMemory(const GpuMemoryCreateInfo &create_info, GpuMemory **gpu_mem);
private:
bool ParseDeviceInfo(void);
void DestroyDeviceInfo(void);
bool CreateDevice(void);
bool DestroyDevice(void);
bool CreatePagingQueue(void);
bool DestroyPagingQueue(void);
void *Lock(D3DKMT_HANDLE handle);
bool Unlock(D3DKMT_HANDLE handle);
bool CreateContext(int engine, D3DKMT_HANDLE *handle);
bool DestroyContext(D3DKMT_HANDLE handle);
void SetPowerOptimization(bool restore);
void InitCmdbufInfo(void);
bool ReserveSystemHeapSpace(void);
bool FreeSystemHeapSpace(void);
bool ReserveLocalHeapSpace(void);
bool CommitSystemHeapSpace(void* addr, int64_t size, bool lock=false);
bool DecommitSystemHeapSpace(void* addr, int64_t size);
bool FreeLocalHeapSpace(void);
void InitVaMgr();
D3DKMT_HANDLE adapter_;
LUID adapter_luid_;
D3DKMT_HANDLE device_;
D3DKMT_HANDLE page_queue_;
D3DKMT_HANDLE page_syncobj_;
uint64_t *page_fence_addr_;
std::atomic<uint64_t> page_fence_value_;
uint64_t local_heap_space_start_;
uint64_t local_heap_space_size_;
uint64_t system_heap_space_start_;
uint64_t system_heap_space_size_;
uint32_t cmdbuf_size_;
uint32_t cmdbuf_aql_frame_size_;
static const uint32_t cmdbuf_aql_frame_num_;
// device info
rocr_proxy::DeviceInfo device_info_;
std::unique_ptr<VaMgr> local_va_mgr_;
//CmdUtil cmd_util;
};
NTSTATUS WDDMGetAdapters(D3DKMT_ADAPTERINFO *&adapters, int &num_adapters);
} // namespace core
} // namespace rocr
#endif
+218
Parādīt failu
@@ -0,0 +1,218 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef _ROCR_GPU_MEMORY_H_
#define _ROCR_GPU_MEMORY_H_
#include <cstddef>
#include <cstdint>
#include "util/utils.h"
#include "inc/wddm/types.h"
#include "inc/wddm/thunks.h"
#include "inc/rocr_proxy/rocr_proxy.h"
namespace rocr {
namespace core {
class WDDMDevice;
union GpuMemoryCreateFlags {
struct {
uint64_t virtual_alloc : 1;
uint64_t physical_only : 1;
uint64_t interprocess : 1;
uint64_t locked : 1;
uint64_t unused : 60;
};
uint64_t reserved;
};
struct GpuMemoryCreateInfo {
GpuMemoryCreateInfo() {
flags.reserved = 0;
domain = rocr_proxy::kLocal;
size = 0;
alignment = 0;
mem_flags = 0;
engine_flag = 0;
va_hint = 0;
user_ptr = nullptr;
dmabuf_fd = -1;
}
GpuMemoryCreateFlags flags;
rocr_proxy::AllocDomain domain;
gpusize size;
gpusize alignment;
int mem_flags;
int engine_flag;
int dmabuf_fd; // Import from dmabuf
void *user_ptr;
gpusize va_hint;
};
struct GpuMemoryDesc {
GpuMemoryDesc() {
gpu_addr = 0;
cpu_addr = nullptr;
client_size = 0;
size = alignment = 0;
flags.reserved = 0;
mem_flags = 0;
engine_flag = 0;
}
rocr_proxy::AllocDomain domain;
LUID adapter_luid; // Where is the backing store location
gpusize gpu_addr;
void *cpu_addr;
gpusize client_size; // user request size
gpusize size;
gpusize alignment;
union {
struct {
uint32_t is_virtual : 1;
uint32_t is_shared : 1;
uint32_t is_external : 1;
uint32_t is_physical_only : 1;
uint32_t is_locked : 1;
uint32_t unused : 27;
};
uint32_t reserved;
} flags;
int mem_flags;
int engine_flag;
};
struct SharedHandleInfo {
rocr_proxy::AllocDomain domain;
LUID adapter_luid;
gpusize client_size; // user request size
uint64_t size;
uint32_t flags;
int mem_flags;
};
using GpuMemoryHandle = void *;
class GpuMemory {
public:
static size_t CalcChunkNumbers(gpusize size);
ErrorCode Init(const GpuMemoryCreateInfo &create_info);
WDDMDevice *GetDevice() const { return device_; }
gpusize Size() const { return desc_.size; }
gpusize ClientSize() const { return desc_.client_size; }
uint64_t GpuAddress() const { return desc_.gpu_addr; }
void *CpuAddress() const { return desc_.cpu_addr; }
inline bool IsLocal() const { return desc_.domain == rocr_proxy::kLocal; }
inline bool IsUserMemory() const { return desc_.domain == rocr_proxy::kUserMemory; }
inline bool IsSystem() const { return desc_.domain == rocr_proxy::kSystem; }
inline bool IsUserQueue() const { return desc_.domain == rocr_proxy::kUserQueue; }
inline bool IsPhysicalOnly() const { return desc_.flags.is_physical_only; }
inline bool IsVirtual() const { return desc_.flags.is_virtual; }
inline bool IsShared() const { return desc_.flags.is_shared; }
inline bool IsExternal() const { return desc_.flags.is_external; }
inline uint32_t Flags() const { return desc_.flags.reserved; }
inline int GetAllocInfo() const { return desc_.mem_flags; }
inline bool IsFineGrain() const { return (desc_.mem_flags & rocr_proxy::kFineGrain); }
inline bool IsSameAdapter(const LUID &luid) const {
return (desc_.adapter_luid.HighPart == luid.HighPart &&
desc_.adapter_luid.LowPart == luid.LowPart);
}
WinAllocationHandle GetAllocationHandle(size_t index) const { return alloc_handles_ptr_[index]; }
size_t NumChunks() const { return num_allocations_; }
const GpuMemoryHandle GetGpuMemoryHandle() const {
return reinterpret_cast<GpuMemoryHandle>(const_cast<GpuMemory*>(this));
}
static GpuMemory *Convert(GpuMemoryHandle handle) { return reinterpret_cast<GpuMemory *>(handle); }
ErrorCode ReserveGpuVirtualAddress(gpusize base_virt_addr, gpusize va_size, gpusize alignment);
ErrorCode FreeGpuVirtualAddress(gpusize va_start_address, gpusize va_size);
ErrorCode MapGpuVirtualAddress(const gpusize map_addr, const gpusize size, gpusize offset = 0);
ErrorCode UnmapGpuVirtualAddress(const gpusize map_addr, const gpusize size, gpusize offset = 0);
ErrorCode MakeResident();
ErrorCode Evict();
ErrorCode ExportPhysicalHandle(int* dmabuf_fd, uint32_t flags = SHARED_ALLOCATION_ALL_ACCESS);
ErrorCode ImportPhysicalHandle(int dmabuf_fd);
~GpuMemory();
protected:
explicit GpuMemory(WDDMDevice *device);
private:
ErrorCode CreatePhysicalMemory();
ErrorCode FreePhysicalMemory();
uint64_t AdjustSize(gpusize size) const;
private:
friend class WDDMDevice;
WDDMDevice *const device_;
GpuMemoryDesc desc_;
size_t num_allocations_;
WinAllocationHandle *alloc_handles_ptr_;
WinAllocationHandle alloc_handle_; // Optimization for num_allocations_ is 1
WinResourceHandle resource_; // Handle to a resource object that wraps the allocation. Used for shared resources
DISALLOW_COPY_AND_ASSIGN(GpuMemory);
};
} // namespace core
} // namespace rocr
#endif
+284
Parādīt failu
@@ -0,0 +1,284 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef _WDDM_QUEUE_H_
#define _WDDM_QUEUE_H_
#include <cinttypes>
#include <condition_variable>
#include "inc/wddm/types.h"
#include "inc/wddm/device.h"
#include "inc/wddm/gpu_memory.h"
#include "hsa-runtime/inc/hsa_ext_amd.h"
#include "hsa-runtime/inc/amd_hsa_queue.h"
#include "hsa-runtime/inc/amd_hsa_signal.h"
#include "inc/wddm/cmd_util.h"
namespace rocr {
namespace core {
class Queue;
class WDDMDevice;
class WDDMQueue {
public:
WDDMQueue(WDDMDevice *device,
uint32_t cmdbuf_size,
uint32_t engine,
bool use_hws = true) :
device(device),
context(NULL),
queue(NULL),
syncobj(NULL),
sync_addr(NULL),
cmdbuf(0),
cmdbuf_addr(0),
cmdbuf_size(cmdbuf_size),
queue_engine(engine),
use_hws(use_hws),
prio(rocr_proxy::kNormal) {
}
virtual ~WDDMQueue() { }
virtual hsa_status_t Init(void) = 0;
virtual hsa_status_t Fini(void) = 0;
hsa_status_t SwsInit(void);
hsa_status_t SwsFini(void);
hsa_status_t SwsSubmit(uint64_t command_addr,
uint64_t command_size,
uint64_t fence_value);
hsa_status_t HwsInit(void);
hsa_status_t HwsFini(void);
hsa_status_t HwsSubmit(uint64_t command_addr,
uint64_t command_size,
uint64_t fence_value);
hsa_status_t SetPriority(hsa_amd_queue_priority_t priority);
uint64_t *GetSyncAddr(void) const { return sync_addr; }
uint64_t GetCmdbufAddr(void) const { return cmdbuf_addr; }
rocr_proxy::SchedLevel ConvertSchedLevel(hsa_amd_queue_priority_t prio) const {
switch (prio) {
case HSA_AMD_QUEUE_PRIORITY_LOW:
return rocr_proxy::kLow;
case HSA_AMD_QUEUE_PRIORITY_HIGH:
return rocr_proxy::kHigh;
case HSA_AMD_QUEUE_PRIORITY_NORMAL:
default:
return rocr_proxy::kNormal;
}
}
WDDMDevice *device;
D3DKMT_HANDLE context;
D3DKMT_HANDLE queue;
D3DKMT_HANDLE syncobj;
uint64_t *sync_addr;
GpuMemoryHandle cmdbuf;
uint64_t cmdbuf_addr;
uint32_t cmdbuf_size;
GpuMemoryHandle queue_mem;
uint64_t queue_addr;
uint32_t queue_engine;
bool use_hws;
rocr_proxy::SchedLevel prio;
};
class ComputeQueue : public WDDMQueue {
public:
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 = true);
~ComputeQueue();
virtual hsa_status_t Init(void);
virtual hsa_status_t Fini(void);
virtual hsa_status_t Submit(void);
void* GetRing(void) const { return ring; }
uint64_t GetRingSize(void) const { return ring_size; }
std::atomic<uint64_t>* GetRingWptr(void) const { return ring_wptr; }
std::atomic<uint64_t>* GetRingRptr(void) const { return ring_rptr; }
uint64_t GetAqlWriteIndex(void) const { return cmdbuf_aql_frame_write_index; }
uint32_t GetAqlFrameSize(void) const { return cmdbuf_aql_frame_size; }
bool IsInvalidPacket(void) const {
uint16_t *packet = (uint16_t *)((char *)ring +
(cmdbuf_aql_frame_write_index % ring_size) * 64);
return ((*packet >> HSA_PACKET_HEADER_TYPE) & ((1 << HSA_PACKET_HEADER_WIDTH_TYPE) - 1))
== HSA_PACKET_TYPE_INVALID;
}
hsa_status_t Process(void);
uint64_t * GetDoorbellPtr() const { return (uint64_t *)&doorbell_signal_.value; }
void RingDoorbell();
private:
hsa_status_t KernelDispatchAqlToPm4(char *cpu, hsa_kernel_dispatch_packet_t *packet);
hsa_status_t BarrierGenericAqlToPm4(char *cpu, hsa_barrier_and_packet_t *packet, bool is_or = false);
struct amd_aql_pm4_ib {
uint16_t header;
uint16_t ven_hdr;
uint32_t ib_jump_cmd[4];
uint32_t dw_cnt_remain;
uint32_t reserved[8];
hsa_signal_t completion_signal;
};
hsa_status_t VendorSpecificAqlToPm4(char *cpu, amd_aql_pm4_ib *packet);
hsa_status_t SwitchAql2PM4(void);
hsa_status_t PreSubmit(void);
hsa_status_t EndSubmit(void);
void *ring;
uint64_t ring_size;
std::atomic<uint64_t> *ring_wptr;
std::atomic<uint64_t> *ring_rptr;
// ib_start_addr is the current ib start address
uint64_t ib_start_addr;
// ib_size is the current ib size.
uint64_t ib_size;
// record the last submitted aql frame write index
uint64_t sync_point;
uint64_t cmdbuf_aql_frame_write_index;
uint32_t cmdbuf_aql_frame_size;
bool needs_barrier;
bool ready_to_submit;
CmdUtil cmd_util;
private:
bool EnableProfiling() {
return AMD_HSA_BITS_GET(amd_queue_rocr_->queue_properties, AMD_QUEUE_PROPERTIES_ENABLE_PROFILING);
}
void HandleError(hsa_status_t status);
bool UpdateScratch(uint32_t private_segment_size, bool wave32);
uint32_t UpdateIndexStride(uint32_t srd, bool wave32);
void *ScratchBase() { return scratch_base_; }
void AppendCmdbufSratchBaseOffset(int offset) {
scratch_base_offset_array_.push_back(offset);
}
bool RelocateCmdbufScratchBase(uint64_t addr);
uint32_t ScratchSizePerWave() { return scratch_size_per_wave_; }
uint64_t GetKernelObjAddr(uint64_t addr) const;
void InitScratchSRD();
GpuMemoryHandle amd_queue_mem_;
amd_queue_t *amd_queue_;
amd_queue_t *amd_queue_rocr_;
amd_signal_t doorbell_signal_;
volatile std::atomic<int64_t> *error_code_;
std::thread aql_to_pm4_thread_;
bool thread_stop_;
std::mutex thread_cond_lock_;
std::condition_variable thread_cond_;
static void AqlToPm4Thread(ComputeQueue *queue);
uint32_t scratch_waves_;
uint32_t scratch_size_per_wave_;
uint32_t scratch_size_;
void *scratch_base_;
GpuMemoryHandle scratch_mem_;
std::vector<int> scratch_base_offset_array_;
};
class SDMAQueue : public WDDMQueue {
public:
SDMAQueue(WDDMDevice *device,
uint64_t cmdbuf_size,
uint32_t engine,
bool use_hws = true) :
WDDMQueue(device, cmdbuf_size, engine, use_hws),
rptr_next(0),
ib_size(0),
ib_start_addr(0) {
}
virtual ~SDMAQueue() { }
hsa_status_t Init(void);
hsa_status_t Fini(void);
hsa_status_t Submit(void);
int PreparePacket(uint32_t offset, uint64_t size);
void WaitQueue(void) {
device->CpuWait(&syncobj, &rptr_next, 1, false);
}
private:
uint64_t rptr_next;
uint64_t ib_size;
uint64_t ib_start_addr;
};
}
}
#endif
+59
Parādīt failu
@@ -0,0 +1,59 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef _ROCR_CORE_INC_WDDM_STATUS_H_
#define _ROCR_CORE_INC_WDDM_STATUS_H_
enum class ErrorCode {
Success,
DeviceLost,
UnSupported,
NotReady,
OutOfMemory,
OutOfGpuMemory,
Timeout,
SyscallFail,
InvalidateParams,
Unknown,
};
#endif
+232
Parādīt failu
@@ -0,0 +1,232 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef _ROCR_CORE_INC_WDDM_THUNKS_H_
#define _ROCR_CORE_INC_WDDM_THUNKS_H_
#include "inc/wddm/status.h"
#include "inc/wddm/types.h"
namespace rocr {
namespace core {
inline ErrorCode TranslateNtStatus(NTSTATUS status) {
switch (status) {
case STATUS_SUCCESS:
return ErrorCode::Success;
case STATUS_PENDING:
return ErrorCode::NotReady;
case STATUS_NO_MEMORY:
return ErrorCode::OutOfMemory;
case STATUS_DEVICE_REMOVED:
return ErrorCode::DeviceLost;
case STATUS_GRAPHICS_NO_VIDEO_MEMORY:
return ErrorCode::OutOfGpuMemory;
case STATUS_TIMEOUT:
return ErrorCode::Timeout;
case STATUS_INVALID_PARAMETER:
return ErrorCode::InvalidateParams;
default:
break;
}
return ErrorCode::Unknown;
}
namespace thunk {
typedef D3DKMT_CREATEALLOCATION CreateAllocationArgs;
typedef D3DKMT_CREATECONTEXT CreateContextArgs;
typedef D3DKMT_CREATECONTEXTVIRTUAL CreateContextVirtualArgs;
typedef D3DKMT_CREATEPAGINGQUEUE CreatePagingQueueArgs;
typedef D3DKMT_CREATESYNCHRONIZATIONOBJECT CreateSynchronizationObjectArgs;
typedef D3DKMT_CREATESYNCHRONIZATIONOBJECT2 CreateSynchronizationObject2Args;
typedef D3DKMT_ESCAPE EscapeArgs;
typedef D3DKMT_EVICT EvictArgs;
typedef D3DKMT_FREEGPUVIRTUALADDRESS FreeGpuVirtualAddressArgs;
typedef D3DKMT_LOCK LockArgs;
typedef D3DKMT_LOCK2 Lock2Args;
typedef D3DKMT_OPENRESOURCE OpenResourceArgs;
typedef D3DKMT_OPENRESOURCEFROMNTHANDLE OpenResourceFromNtHandleArgs;
typedef D3DKMT_QUERYADAPTERINFO QueryAdapterInfoArgs;
typedef D3DKMT_SIGNALSYNCHRONIZATIONOBJECT SignalSynchronizationObjectArgs;
typedef D3DKMT_SIGNALSYNCHRONIZATIONOBJECT2 SignalSynchronizationObject2Args;
typedef D3DKMT_SIGNALSYNCHRONIZATIONOBJECTFROMCPU SignalSynchronizationObjectFromCpuArgs;
typedef D3DKMT_SIGNALSYNCHRONIZATIONOBJECTFROMGPU2 SignalSynchronizationObjectFromGpuArgs;
typedef D3DKMT_SUBMITCOMMAND SubmitCommandArgs;
typedef D3DKMT_UNLOCK UnlockArgs;
typedef D3DKMT_UNLOCK2 Unlock2Args;
typedef D3DKMT_UPDATEGPUVIRTUALADDRESS UpdateGpuVirtualAddressArgs;
typedef D3DKMT_WAITFORSYNCHRONIZATIONOBJECT WaitForSynchronizationObjectArgs;
typedef D3DKMT_WAITFORSYNCHRONIZATIONOBJECT2 WaitForSynchronizationObject2Args;
typedef D3DKMT_WAITFORSYNCHRONIZATIONOBJECTFROMCPU WaitForSynchronizationObjectFromCpuArgs;
typedef D3DKMT_WAITFORSYNCHRONIZATIONOBJECTFROMGPU WaitForSynchronizationObjectFromGpuArgs;
typedef D3DKMT_ACQUIREKEYEDMUTEX AcquireKeyedMutexArgs;
typedef D3DKMT_RELEASEKEYEDMUTEX ReleaseKeyedMutexArgs;
typedef D3DKMT_OPENKEYEDMUTEX OpenKeyedMutexArgs;
typedef D3DKMT_DESTROYKEYEDMUTEX DestroyKeyedMutexArgs;
typedef D3DKMT_QUERYVIDEOMEMORYINFO QueryVideoMemoryInfoArgs;
typedef D3DKMT_CREATEHWQUEUE CreateHwQueueArgs;
typedef D3DKMT_DESTROYHWQUEUE DestroyHwQueueArgs;
typedef D3DKMT_SUBMITCOMMANDTOHWQUEUE SubmitCommandToHwQueueArgs;
typedef D3DKMT_SUBMITPRESENTTOHWQUEUE SubmitPresentToHwQueueArgs;
typedef D3DKMT_SUBMITSIGNALSYNCOBJECTSTOHWQUEUE SubmitSignalSyncObjectsToHwQueueArgs;
typedef D3DKMT_SUBMITWAITFORSYNCOBJECTSTOHWQUEUE SubmitWaitForSyncObjectsToHwQueueArgs;
typedef D3DKMT_CREATESYNCFILE CreateSyncFileArgs;
inline ErrorCode MapGpuVirtualAddress(D3DDDI_MAPGPUVIRTUALADDRESS *args) {
return TranslateNtStatus(D3DKMTMapGpuVirtualAddress(args));
}
inline ErrorCode CreateAllocation(CreateAllocationArgs *args) {
return TranslateNtStatus(D3DKMTCreateAllocation2(args));
}
inline ErrorCode DestroyAllocation(
WinDeviceHandle device,
WinResourceHandle resource,
size_t num_allocations,
const WinAllocationHandle *alloc_handles) {
D3DKMT_DESTROYALLOCATION2 args{};
memset(&args, 0, sizeof(args));
args.hDevice = device;
if (resource) {
args.hResource = resource;
} else {
args.phAllocationList = alloc_handles;
args.AllocationCount = num_allocations;
}
return TranslateNtStatus(D3DKMTDestroyAllocation2(&args));
}
inline ErrorCode ReserveGpuVirtualAddress(D3DDDI_RESERVEGPUVIRTUALADDRESS *args) {
return TranslateNtStatus(D3DKMTReserveGpuVirtualAddress(args));
}
inline ErrorCode ReserveGpuVirtualAddress(WinAdapterHandle handle,
gpusize size,
gpusize base_address,
gpusize *out_addr) {
D3DDDI_RESERVEGPUVIRTUALADDRESS args{};
args.hPagingQueue = handle;
args.Size = size;
args.BaseAddress = base_address;
auto code = ReserveGpuVirtualAddress(&args);
if (code == ErrorCode::Success)
*out_addr = args.VirtualAddress;
return code;
}
inline ErrorCode ReserveGpuVirtualAddress(WinAdapterHandle handle,
gpusize size,
gpusize minimum_address,
gpusize maximum_address,
gpusize *out_addr) {
D3DDDI_RESERVEGPUVIRTUALADDRESS args{};
args.hPagingQueue = handle;
args.Size = size;
args.MinimumAddress = minimum_address;
args.MaximumAddress = maximum_address;
auto code = ReserveGpuVirtualAddress(&args);
if (code == ErrorCode::Success)
*out_addr = args.VirtualAddress;
return code;
}
inline ErrorCode FreeGpuVirtualAddress(FreeGpuVirtualAddressArgs *args) {
return TranslateNtStatus(D3DKMTFreeGpuVirtualAddress(args));
}
inline ErrorCode FreeGpuVirtualAddress(WinAdapterHandle handle,
gpusize base_address,
gpusize size) {
FreeGpuVirtualAddressArgs args{};
args.hAdapter = handle;
args.Size = size;
args.BaseAddress = base_address;
return FreeGpuVirtualAddress(&args);
}
inline ErrorCode MakeResident(D3DDDI_MAKERESIDENT *args) {
return TranslateNtStatus(D3DKMTMakeResident(args));
}
inline ErrorCode Evict(EvictArgs *args) {
return TranslateNtStatus(D3DKMTEvict(args));
}
inline ErrorCode ShareObjects(size_t num_allocations,
WinResourceHandle resource,
uint32_t flags,
int* dmabuf_fd) {
OBJECT_ATTRIBUTES obj_attr;
HANDLE nt_handle;
ErrorCode ret;
InitializeObjectAttributes(&obj_attr, nullptr, OBJ_INHERIT, nullptr, nullptr);
ret = TranslateNtStatus(D3DKMTShareObjects(num_allocations,
&resource, &obj_attr, flags, &nt_handle));
if (ret == ErrorCode::Success)
*dmabuf_fd = *(reinterpret_cast<int*>(&nt_handle));
else
*dmabuf_fd = -1;
return ret;
}
inline ErrorCode QueryResourceInfoFromNtHandle(D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE *args) {
return TranslateNtStatus(D3DKMTQueryResourceInfoFromNtHandle(args));
}
inline ErrorCode OpenResourceFromNtHandle(D3DKMT_OPENRESOURCEFROMNTHANDLE *args) {
return TranslateNtStatus(D3DKMTOpenResourceFromNtHandle(args));
}
} // namespace thunk
} // namespace core
} // namespace rocr
#endif // _ROCR_CORE_INC_WDDM_THUNKS_H_
+101
Parādīt failu
@@ -0,0 +1,101 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef _ROCR_CORE_INC_WDDM_TYPES_H_
#define _ROCR_CORE_INC_WDDM_TYPES_H_
#include <cstdint>
#include <ntstatus.h>
#include "inc/rocr_proxy/wddm_types.h"
// windows wchar is 16bit, but linux is 32bit
// seems libdxcore (not dxgkrnl.ko) convert thunk windows wchar to linux one
// so only accept 32bit wchar args. note driver private data structure still
// use 16bit wchar
#define WCHAR wchar_t
#define PCWSTR const wchar_t *
#include <d3dkmthk.h>
#undef WCHAR
#undef PCWSTR
using gpusize = uint64_t; // Used to specify GPU addresses and sizes of GPU allocations
using WinAllocationHandle = D3DKMT_HANDLE;
using WinResourceHandle = D3DKMT_HANDLE;
using WinContextHandle = D3DKMT_HANDLE;
using WinDeviceHandle = D3DKMT_HANDLE;
using WinAdapterHandle = D3DKMT_HANDLE;
//reference dk/winnt.h
#define STANDARD_RIGHTS_REQUIRED (0x000F0000L)
//reference dk/ntdef.h
#define OBJ_INHERIT (0x00000002L)
typedef WCHAR *PWCHAR, *LPWCH, *PWCH;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
#else // MIDL_PASS
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
#define InitializeObjectAttributes( p, n, a, r, s ) { \
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
(p)->RootDirectory = r; \
(p)->Attributes = a; \
(p)->ObjectName = n; \
(p)->SecurityDescriptor = s; \
(p)->SecurityQualityOfService = NULL; \
}
#endif // _ROCR_CORE_INC_WDDM_TYPES_H_
+86
Parādīt failu
@@ -0,0 +1,86 @@
#ifndef VA_MGR_H_
#define VA_MGR_H_
#include <mutex>
#include <map>
#include "util/utils.h"
namespace rocr {
namespace core {
class VaMgr {
public:
VaMgr(uint64_t start, uint64_t size, uint64_t min_align);
~VaMgr();
/* Allocate `bytes` VA, if `align` is not zero, the returned address is aligned by `align`.
* If `addr` parameter is not zero, try best to allocate VA from fixed address `addr`.
*/
uint64_t Alloc(uint64_t bytes, uint64_t align, uint64_t addr = 0);
void Free(uint64_t addr);
private:
uint64_t AllocImpl(uint64_t bytes, uint64_t align);
struct Fragment {
using ptr = std::multimap<uint64_t, uint64_t>::iterator;
ptr free_list_entry_;
struct {
uint64_t size : 63;
bool is_free : 1;
};
Fragment() : size(0), is_free(false) {}
Fragment(ptr iterator, uint64_t len, bool is_free)
: free_list_entry_(iterator), size(len), is_free(is_free) {}
};
static inline Fragment make_fragment(typename Fragment::ptr iter, uint64_t len) {
return {iter, len, true};
}
inline Fragment make_fragment(uint64_t len) { return {free_list_.end(), len, false}; }
static inline bool is_free(const Fragment& f) { return f.is_free; }
void set_used(Fragment& f) {
f.is_free = false;
f.free_list_entry_ = free_list_.end();
}
static void set_free(Fragment& f, typename Fragment::ptr iter) {
f.free_list_entry_ = iter;
f.is_free = true;
}
inline void remove_free_list_entry(Fragment& frag) {
if (frag.free_list_entry_ != free_list_.end()) {
free_list_.erase(frag.free_list_entry_);
frag.free_list_entry_ = free_list_.end();
}
}
inline void add_free_fragment(uint64_t size, uint64_t base) {
auto it = free_list_.insert(std::make_pair(size, base));
frag_map_[base] = make_fragment(it, size);
}
inline void add_used_fragment(uint64_t size, uint64_t base) {
frag_map_[base] = make_fragment(size);
}
// Indexed by size
std::multimap<uint64_t, uint64_t> free_list_;
// Indexed by VA, each fragment has no overlap
std::map<uint64_t, Fragment> frag_map_;
uint64_t min_align_;
std::mutex lock_; // Mutex protecting allocation and free of va
DISALLOW_COPY_AND_ASSIGN(VaMgr);
};
} // namespace core
} // namespace rocr
#endif // VA_MGR_H_
+69
Parādīt failu
@@ -0,0 +1,69 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#include <cstdint>
#include <amdgpu.h>
#include <amdgpu_drm.h>
#include "inc/wddm/types.h"
#include "inc/wddm/device.h"
#include "libhsakmt.h"
HSAKMT_STATUS HSAKMTAPI hsaKmtGetAMDGPUDeviceHandle(
HSAuint32 NodeId, HsaAMDGPUDeviceHandle *DeviceHandle) {
CHECK_DXG_OPEN();
rocr::core::WDDMDevice *pDevice = get_wddmdev(NodeId);
if (pDevice != nullptr) {
*DeviceHandle = reinterpret_cast<HsaAMDGPUDeviceHandle>(pDevice);
return HSAKMT_STATUS_SUCCESS;
}
return HSAKMT_STATUS_ERROR;
}
HSAKMTAPI int hsaKmtamdgpu_query_gpu_info(void *dev,
struct amdgpu_gpu_info *info) {
rocr::core::WDDMDevice *pDevice =
reinterpret_cast<rocr::core::WDDMDevice *>(dev);
memset(info, 0, sizeof(*info));
info->gpu_counter_freq = pDevice->GPUCounterFrequency() / 1000ull;
return 0;
}
+158
Parādīt failu
@@ -0,0 +1,158 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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 LIBHSAKMT_H_INCLUDED
#define LIBHSAKMT_H_INCLUDED
#include <pthread.h>
#include <stdint.h>
#include <limits.h>
#include "hsakmt/hsakmt.h"
#include "inc/wddm/types.h"
#include "inc/wddm/device.h"
rocr::core::WDDMDevice* get_wddmdev(uint32_t node_id);
extern unsigned long dxg_open_count;
extern bool hsakmt_forked;
extern pthread_mutex_t hsakmt_mutex;
extern bool is_dgpu;
extern bool is_svm_api_supported;
extern int zfb_support;
extern int vendor_packet_support;
#undef HSAKMTAPI
#define HSAKMTAPI __attribute__((visibility ("default")))
#if defined(__clang__)
#if __has_feature(address_sanitizer)
#define SANITIZER_AMDGPU 1
#endif
#endif
/*Avoid pointer-to-int-cast warning*/
#define PORT_VPTR_TO_UINT64(vptr) ((uint64_t)(unsigned long)(vptr))
/*Avoid int-to-pointer-cast warning*/
#define PORT_UINT64_TO_VPTR(v) ((void*)(unsigned long)(v))
#define CHECK_DXG_OPEN() \
do { if (dxg_open_count == 0 || hsakmt_forked) return HSAKMT_STATUS_KERNEL_IO_CHANNEL_NOT_OPENED; } while (0)
/* Might be defined in limits.h on platforms where it is constant (used by musl) */
/* See also: https://pubs.opengroup.org/onlinepubs/7908799/xsh/limits.h.html */
#ifndef PAGE_SIZE
extern int PAGE_SIZE;
#endif
extern int PAGE_SHIFT;
/* 64KB BigK fragment size for TLB efficiency */
#define GPU_BIGK_PAGE_SIZE (1 << 16)
/* 2MB huge page size for 4-level page tables on Vega10 and later GPUs */
#define GPU_HUGE_PAGE_SIZE (2 << 20)
#define CHECK_PAGE_MULTIPLE(x) \
do { if ((uint64_t)PORT_VPTR_TO_UINT64(x) % PAGE_SIZE) return HSAKMT_STATUS_INVALID_PARAMETER; } while(0)
#define ALIGN_UP(x,align) (((uint64_t)(x) + (align) - 1) & ~(uint64_t)((align)-1))
#define ALIGN_UP_32(x,align) (((uint32_t)(x) + (align) - 1) & ~(uint32_t)((align)-1))
#define PAGE_ALIGN_UP(x) ALIGN_UP(x,PAGE_SIZE)
#define BITMASK(n) ((n) ? (UINT64_MAX >> (sizeof(UINT64_MAX) * CHAR_BIT - (n))) : 0)
#define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
/* HSA Thunk logging usage */
extern int hsakmt_debug_level;
#define hsakmt_print(level, fmt, ...) \
do { if (level <= hsakmt_debug_level) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)
#define HSAKMT_DEBUG_LEVEL_DEFAULT -1
#define HSAKMT_DEBUG_LEVEL_ERR 3
#define HSAKMT_DEBUG_LEVEL_WARNING 4
#define HSAKMT_DEBUG_LEVEL_INFO 6
#define HSAKMT_DEBUG_LEVEL_DEBUG 7
#define pr_err(fmt, ...) \
hsakmt_print(HSAKMT_DEBUG_LEVEL_ERR, fmt, ##__VA_ARGS__)
#define pr_warn(fmt, ...) \
hsakmt_print(HSAKMT_DEBUG_LEVEL_WARNING, fmt, ##__VA_ARGS__)
#define pr_info(fmt, ...) \
hsakmt_print(HSAKMT_DEBUG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define pr_debug(fmt, ...) \
hsakmt_print(HSAKMT_DEBUG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
#define pr_err_once(fmt, ...) \
({ \
static bool __print_once; \
if (!__print_once) { \
__print_once = true; \
pr_err(fmt, ##__VA_ARGS__); \
} \
})
#define pr_warn_once(fmt, ...) \
({ \
static bool __print_once; \
if (!__print_once) { \
__print_once = true; \
pr_warn(fmt, ##__VA_ARGS__); \
} \
})
/* Expects HSA_ENGINE_ID.ui32, returns gfxv (full) in hex */
#define HSA_GET_GFX_VERSION_FULL(ui32) \
(((ui32.Major) << 16) | ((ui32.Minor) << 8) | (ui32.Stepping))
HSAKMT_STATUS validate_nodeid(uint32_t nodeid, uint32_t *gpu_id);
HSAKMT_STATUS gpuid_to_nodeid(uint32_t gpu_id, uint32_t* node_id);
bool prefer_ats(HSAuint32 node_id);
uint16_t get_device_id_by_node_id(HSAuint32 node_id);
uint16_t get_device_id_by_gpu_id(HSAuint32 gpu_id);
uint32_t get_direct_link_cpu(uint32_t gpu_node);
HSAKMT_STATUS topology_sysfs_get_system_props(HsaSystemProperties *props);
HSAKMT_STATUS topology_get_node_props(HSAuint32 NodeId,
HsaNodeProperties *NodeProperties);
HSAKMT_STATUS topology_get_iolink_props(HSAuint32 NodeId,
HSAuint32 NumIoLinks,
HsaIoLinkProperties *IoLinkProperties);
void topology_setup_is_dgpu_param(HsaNodeProperties *props);
HSAuint32 PageSizeFromFlags(unsigned int pageSizeFlags);
#define MIN(a, b) ({ \
typeof(a) tmp1 = (a), tmp2 = (b); \
tmp1 < tmp2 ? tmp1 : tmp2; })
#define MAX(a, b) ({ \
typeof(a) tmp1 = (a), tmp2 = (b); \
tmp1 > tmp2 ? tmp1 : tmp2; })
uint32_t get_num_sysfs_nodes(void);
bool is_forked_child(void);
/* Calculate VGPR and SGPR register file size per CU */
uint32_t get_vgpr_size_per_cu(HSA_ENGINE_ID id);
#define SGPR_SIZE_PER_CU 0x4000
#endif
+97
Parādīt failu
@@ -0,0 +1,97 @@
HSAKMT_1
{
global:
hsaKmtOpenKFD;
hsaKmtCloseKFD;
hsaKmtGetVersion;
hsaKmtAcquireSystemProperties;
hsaKmtReleaseSystemProperties;
hsaKmtGetNodeProperties;
hsaKmtGetNodeMemoryProperties;
hsaKmtGetNodeCacheProperties;
hsaKmtGetNodeIoLinkProperties;
hsaKmtCreateEvent;
hsaKmtDestroyEvent;
hsaKmtSetEvent;
hsaKmtResetEvent;
hsaKmtQueryEventState;
hsaKmtWaitOnEvent;
hsaKmtWaitOnMultipleEvents;
hsaKmtCreateQueue;
hsaKmtUpdateQueue;
hsaKmtDestroyQueue;
hsaKmtSetQueueCUMask;
hsaKmtSetMemoryPolicy;
hsaKmtAllocMemory;
hsaKmtAllocMemoryAlign;
hsaKmtFreeMemory;
hsaKmtAvailableMemory;
hsaKmtRegisterMemory;
hsaKmtRegisterMemoryToNodes;
hsaKmtRegisterMemoryWithFlags;
hsaKmtRegisterGraphicsHandleToNodes;
hsaKmtShareMemory;
hsaKmtRegisterSharedHandle;
hsaKmtRegisterSharedHandleToNodes;
hsaKmtProcessVMRead;
hsaKmtProcessVMWrite;
hsaKmtDeregisterMemory;
hsaKmtMapMemoryToGPU;
hsaKmtMapMemoryToGPUNodes;
hsaKmtUnmapMemoryToGPU;
hsaKmtDbgRegister;
hsaKmtDbgUnregister;
hsaKmtDbgWavefrontControl;
hsaKmtDbgAddressWatch;
hsaKmtDbgEnable;
hsaKmtDbgDisable;
hsaKmtDbgGetDeviceData;
hsaKmtDbgGetQueueData;
hsaKmtGetClockCounters;
hsaKmtPmcGetCounterProperties;
hsaKmtPmcRegisterTrace;
hsaKmtPmcUnregisterTrace;
hsaKmtPmcAcquireTraceAccess;
hsaKmtPmcReleaseTraceAccess;
hsaKmtPmcStartTrace;
hsaKmtPmcQueryTrace;
hsaKmtPmcStopTrace;
hsaKmtMapGraphicHandle;
hsaKmtUnmapGraphicHandle;
hsaKmtSetTrapHandler;
hsaKmtGetTileConfig;
hsaKmtQueryPointerInfo;
hsaKmtSetMemoryUserData;
hsaKmtGetQueueInfo;
hsaKmtAllocQueueGWS;
hsaKmtRuntimeEnable;
hsaKmtRuntimeDisable;
hsaKmtCheckRuntimeDebugSupport;
hsaKmtGetRuntimeCapabilities;
hsaKmtDebugTrapIoctl;
hsaKmtSPMAcquire;
hsaKmtSPMRelease;
hsaKmtSPMSetDestBuffer;
hsaKmtSVMSetAttr;
hsaKmtSVMGetAttr;
hsaKmtSetXNACKMode;
hsaKmtGetXNACKMode;
hsaKmtOpenSMI;
hsaKmtExportDMABufHandle;
hsaKmtWaitOnEvent_Ext;
hsaKmtWaitOnMultipleEvents_Ext;
hsaKmtReplaceAsanHeaderPage;
hsaKmtReturnAsanHeaderPage;
hsaKmtGetAMDGPUDeviceHandle;
hsaKmtPcSamplingQueryCapabilities;
hsaKmtPcSamplingCreate;
hsaKmtPcSamplingDestroy;
hsaKmtPcSamplingStart;
hsaKmtPcSamplingStop;
hsaKmtPcSamplingSupport;
hsaKmtGetVersionCapInfo;
hsaKmtQueueRingDoorbell;
hsaKmtamdgpu_query_gpu_info;
local: *;
};
+554
Parādīt failu
@@ -0,0 +1,554 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include "libhsakmt.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/sysinfo.h>
#include <fcntl.h>
#include "inc/wddm/gpu_memory.h"
struct Allocation {
Allocation()
: handle(0), cpu_addr(0), gpu_addr(0), size(0), userptr(false),
user_data(nullptr), size_requested(0), node_id(0), mem_flags_value(0) {}
Allocation(rocr::core::GpuMemoryHandle handle_arg, void *cpu_addr_arg,
uint64_t gpu_addr_arg, size_t size_arg, bool userptr_arg = false,
void *user_data_arg = nullptr, size_t user_size_arg = 0,
HSAuint32 node_id_arg = 0, HSAuint32 mem_flags_value_arg = 0)
: handle(handle_arg), cpu_addr(cpu_addr_arg), gpu_addr(gpu_addr_arg),
size(size_arg), userptr(userptr_arg), user_data(user_data_arg),
size_requested(user_size_arg), node_id(node_id_arg),
mem_flags_value(mem_flags_value_arg) {}
rocr::core::GpuMemoryHandle handle;
void *cpu_addr;
uint64_t gpu_addr;
bool userptr;
size_t size; /* actual size = align_up(size_requested, granularity) */
void *user_data;
size_t size_requested; /* size requested by user */
HSAuint32 node_id;
HSAuint32 mem_flags_value;
};
static std::map<const void *, Allocation> allocation_map_;
static std::mutex allocation_map_lock_;
HSAKMT_STATUS HSAKMTAPI hsaKmtSetMemoryPolicy(HSAuint32 Node,
HSAuint32 DefaultPolicy,
HSAuint32 AlternatePolicy,
void *MemoryAddressAlternate,
HSAuint64 MemorySizeInBytes) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAuint32 PageSizeFromFlags(unsigned int pageSizeFlags) {
switch (pageSizeFlags) {
case HSA_PAGE_SIZE_4KB:
return 4 * 1024;
case HSA_PAGE_SIZE_64KB:
return 64 * 1024;
case HSA_PAGE_SIZE_2MB:
return 2 * 1024 * 1024;
case HSA_PAGE_SIZE_1GB:
return 1024 * 1024 * 1024;
default:
assert(false);
return 4 * 1024;
}
}
HSAKMT_STATUS HSAKMTAPI hsaKmtAllocMemory(HSAuint32 PreferredNode,
HSAuint64 SizeInBytes,
HsaMemFlags MemFlags,
void **MemoryAddress) {
return hsaKmtAllocMemoryAlign(PreferredNode, SizeInBytes, 0, MemFlags,
MemoryAddress);
}
#define POWER_OF_2(x) ((x && (!(x & (x - 1)))) ? 1 : 0)
bool isSystemMemoryAvailable(HSAuint64 SizeInBytes) {
struct sysinfo info;
if (sysinfo(&info) != 0)
return false;
return SizeInBytes <= info.freeram;
}
bool isLocalMemoryAvailable(rocr::core::WDDMDevice *dev,
HSAuint64 SizeInBytes) {
return SizeInBytes <= dev->VramAvail();
}
HSAKMT_STATUS HSAKMTAPI hsaKmtAllocMemoryAlign(HSAuint32 PreferredNode,
HSAuint64 SizeInBytes,
HSAuint64 Alignment,
HsaMemFlags MemFlags,
void **MemoryAddress) {
CHECK_DXG_OPEN();
if (!MemoryAddress)
return HSAKMT_STATUS_INVALID_PARAMETER;
if (MemFlags.ui32.FixedAddress) {
if (*MemoryAddress == nullptr)
return HSAKMT_STATUS_INVALID_PARAMETER;
} else
*MemoryAddress = nullptr;
rocr::core::WDDMDevice *dev = get_wddmdev(1);
if (!dev)
return HSAKMT_STATUS_ERROR;
rocr::core::GpuMemory *gpu_mem = nullptr;
rocr::core::GpuMemoryCreateInfo create_info{};
create_info.size = SizeInBytes;
if (!MemFlags.ui32.NonPaged || zfb_support || MemFlags.ui32.GTTAccess) {
/* If allocate VRAM under ZFB mode */
if (zfb_support && MemFlags.ui32.NonPaged == 1)
MemFlags.ui32.CoarseGrain = 1;
create_info.domain = rocr_proxy::AllocDomain::kSystem;
if (!isSystemMemoryAvailable(SizeInBytes))
return HSAKMT_STATUS_NO_MEMORY;
} else {
create_info.domain = rocr_proxy::AllocDomain::kLocal;
if (!isLocalMemoryAvailable(dev, SizeInBytes))
return HSAKMT_STATUS_NO_MEMORY;
}
if (!MemFlags.ui32.CoarseGrain)
create_info.mem_flags = rocr_proxy::kFineGrain;
// create_info.mem_flags |= rocr_proxy::kKernarg;
create_info.flags.physical_only = MemFlags.ui32.NoAddress;
create_info.flags.interprocess = MemFlags.ui32.NoAddress;
create_info.flags.locked = 0; //!!(alloc_flags & AllocatePinned);
auto code = dev->CreateGpuMemory(create_info, &gpu_mem);
if (code == ErrorCode::Success) {
*MemoryAddress = reinterpret_cast<void *>(gpu_mem->GpuAddress());
std::lock_guard<std::mutex> gard(allocation_map_lock_);
allocation_map_[*MemoryAddress] = Allocation(
gpu_mem->GetGpuMemoryHandle(), *MemoryAddress, (uint64_t)*MemoryAddress,
create_info.size, false, nullptr, SizeInBytes,
MemFlags.ui32.GTTAccess ? 0 : PreferredNode, MemFlags.Value);
return HSAKMT_STATUS_SUCCESS;
}
return HSAKMT_STATUS_ERROR;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtFreeMemory(void *MemoryAddress,
HSAuint64 SizeInBytes) {
CHECK_DXG_OPEN();
if (!MemoryAddress)
return HSAKMT_STATUS_INVALID_PARAMETER;
rocr::core::GpuMemory *gpu_mem = nullptr;
{
std::lock_guard<std::mutex> gard(allocation_map_lock_);
auto it = allocation_map_.find(MemoryAddress);
if (it == allocation_map_.end()) {
return HSAKMT_STATUS_ERROR;
}
gpu_mem = rocr::core::GpuMemory::Convert(it->second.handle);
allocation_map_.erase(it);
}
delete gpu_mem;
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtAvailableMemory(HSAuint32 Node,
HSAuint64 *AvailableBytes) {
CHECK_DXG_OPEN();
if (!AvailableBytes)
return HSAKMT_STATUS_INVALID_PARAMETER;
rocr::core::WDDMDevice *dev = get_wddmdev(Node);
if (!dev)
return HSAKMT_STATUS_ERROR;
*AvailableBytes = dev->VramAvail();
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtRegisterMemory(void *MemoryAddress,
HSAuint64 MemorySizeInBytes) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtRegisterMemoryToNodes(void *MemoryAddress,
HSAuint64 MemorySizeInBytes,
HSAuint64 NumberOfNodes,
HSAuint32 *NodeArray) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtRegisterMemoryWithFlags(
void *MemoryAddress, HSAuint64 MemorySizeInBytes, HsaMemFlags MemFlags) {
CHECK_DXG_OPEN();
if (!MemoryAddress)
return HSAKMT_STATUS_INVALID_PARAMETER;
pr_debug("[%s] address %p\n", __func__, MemoryAddress);
if (MemFlags.ui32.ExtendedCoherent && MemFlags.ui32.CoarseGrain)
return HSAKMT_STATUS_INVALID_PARAMETER;
// Registered memory should be ordinary paged host memory.
if ((MemFlags.ui32.HostAccess != 1) || (MemFlags.ui32.NonPaged == 1))
return HSAKMT_STATUS_NOT_SUPPORTED;
if (!is_dgpu)
/* TODO: support mixed APU and dGPU configurations */
return HSAKMT_STATUS_NOT_SUPPORTED;
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtRegisterGraphicsHandleToNodes(
HSAuint64 GraphicsResourceHandle,
HsaGraphicsResourceInfo *GraphicsResourceInfo, HSAuint64 NumberOfNodes,
HSAuint32 *NodeArray) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtExportDMABufHandle(void *MemoryAddress,
HSAuint64 MemorySizeInBytes,
int *DMABufFd,
HSAuint64 *Offset) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI
hsaKmtShareMemory(void *MemoryAddress, HSAuint64 SizeInBytes,
HsaSharedMemoryHandle *SharedMemoryHandle) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI
hsaKmtRegisterSharedHandle(const HsaSharedMemoryHandle *SharedMemoryHandle,
void **MemoryAddress, HSAuint64 *SizeInBytes) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtRegisterSharedHandleToNodes(
const HsaSharedMemoryHandle *SharedMemoryHandle, void **MemoryAddress,
HSAuint64 *SizeInBytes, HSAuint64 NumberOfNodes, HSAuint32 *NodeArray) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtProcessVMRead(HSAuint32 Pid,
HsaMemoryRange *LocalMemoryArray,
HSAuint64 LocalMemoryArrayCount,
HsaMemoryRange *RemoteMemoryArray,
HSAuint64 RemoteMemoryArrayCount,
HSAuint64 *SizeCopied) {
pr_err("[%s] Deprecated\n", __func__);
assert(false);
return HSAKMT_STATUS_NOT_IMPLEMENTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtProcessVMWrite(HSAuint32 Pid,
HsaMemoryRange *LocalMemoryArray,
HSAuint64 LocalMemoryArrayCount,
HsaMemoryRange *RemoteMemoryArray,
HSAuint64 RemoteMemoryArrayCount,
HSAuint64 *SizeCopied) {
pr_err("[%s] Deprecated\n", __func__);
assert(false);
return HSAKMT_STATUS_NOT_IMPLEMENTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDeregisterMemory(void *MemoryAddress) {
CHECK_DXG_OPEN();
if (!MemoryAddress)
return HSAKMT_STATUS_INVALID_PARAMETER;
pr_debug("[%s] address %p\n", __func__, MemoryAddress);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtMapMemoryToGPU(void *MemoryAddress,
HSAuint64 MemorySizeInBytes,
HSAuint64 *AlternateVAGPU) {
CHECK_DXG_OPEN();
if (!MemoryAddress || !AlternateVAGPU) {
pr_err("FIXME: mapping NULL pointer\n");
return HSAKMT_STATUS_ERROR;
}
uint64_t start = rocr::AlignDown((uint64_t)MemoryAddress, 4096);
uint64_t end =
rocr::AlignUp((uint64_t)MemoryAddress + MemorySizeInBytes, 4096);
void *aligned_ptr = (void *)start;
size_t aligned_size = end - start;
{
std::lock_guard<std::mutex> gard(allocation_map_lock_);
// GTT mem
auto it_gtt = allocation_map_.find(aligned_ptr);
if (it_gtt != allocation_map_.end()) {
if (!it_gtt->second.userptr) {
if (it_gtt->second.size >= MemorySizeInBytes) {
*AlternateVAGPU = (uint64_t)MemoryAddress;
return HSAKMT_STATUS_SUCCESS;
} else {
return HSAKMT_STATUS_ERROR;
}
}
}
// userptr mem
auto it = allocation_map_.find(MemoryAddress);
if (it != allocation_map_.end()) {
if (it->second.userptr && it->second.size >= MemorySizeInBytes) {
*AlternateVAGPU =
(uintptr_t)it->second.gpu_addr +
((uintptr_t)MemoryAddress - (uintptr_t)it->second.cpu_addr);
return HSAKMT_STATUS_SUCCESS;
}
}
}
rocr::core::WDDMDevice *dev = get_wddmdev(1);
if (!dev)
return HSAKMT_STATUS_ERROR;
rocr::core::GpuMemory *gpu_mem = nullptr;
rocr::core::GpuMemoryHandle handle = 0;
uint64_t addr;
rocr::core::GpuMemoryCreateInfo create_info{};
create_info.domain = rocr_proxy::kUserMemory;
create_info.size = aligned_size;
create_info.user_ptr = aligned_ptr;
auto code = dev->CreateGpuMemory(create_info, &gpu_mem);
if (code == ErrorCode::Success) {
addr = gpu_mem->GpuAddress();
handle = gpu_mem->GetGpuMemoryHandle();
} else {
return HSAKMT_STATUS_ERROR;
}
{
std::lock_guard<std::mutex> guard(allocation_map_lock_);
allocation_map_[MemoryAddress] =
Allocation(handle, aligned_ptr, addr, aligned_size, true, MemoryAddress,
MemorySizeInBytes);
allocation_map_[(void *)addr] =
Allocation(handle, aligned_ptr, addr, aligned_size, true, nullptr,
MemorySizeInBytes);
}
*AlternateVAGPU = addr + ((uintptr_t)MemoryAddress - (uintptr_t)aligned_ptr);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtMapMemoryToGPUNodes(
void *MemoryAddress, HSAuint64 MemorySizeInBytes, HSAuint64 *AlternateVAGPU,
HsaMemMapFlags MemMapFlags, HSAuint64 NumberOfNodes, HSAuint32 *NodeArray) {
return hsaKmtMapMemoryToGPU(MemoryAddress, MemorySizeInBytes, AlternateVAGPU);
}
HSAKMT_STATUS HSAKMTAPI hsaKmtUnmapMemoryToGPU(void *MemoryAddress) {
CHECK_DXG_OPEN();
if (!MemoryAddress) {
/* Workaround for runtime bug */
pr_err("FIXME: Unmapping NULL pointer\n");
return HSAKMT_STATUS_SUCCESS;
}
pr_debug("[%s] address %p\n", __func__, MemoryAddress);
rocr::core::GpuMemoryHandle handle = nullptr;
{
std::lock_guard<std::mutex> gard(allocation_map_lock_);
auto it = allocation_map_.find(MemoryAddress);
if (it == allocation_map_.end()) {
return HSAKMT_STATUS_ERROR;
}
if (!it->second.userptr) {
return HSAKMT_STATUS_SUCCESS;
}
handle = it->second.handle;
allocation_map_.erase((void *)it->second.gpu_addr);
allocation_map_.erase(it);
}
auto gpu_mem = rocr::core::GpuMemory::Convert(handle);
delete gpu_mem;
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtMapGraphicHandle(HSAuint32 NodeId,
HSAuint64 GraphicDeviceHandle,
HSAuint64 GraphicResourceHandle,
HSAuint64 GraphicResourceOffset,
HSAuint64 GraphicResourceSize,
HSAuint64 *FlatMemoryAddress) {
/* This API was only ever implemented in KFD for Kaveri and
* was never upstreamed. There are no open-source users of
* this interface. It has been superseded by
* RegisterGraphicsHandleToNodes.
*/
return HSAKMT_STATUS_NOT_IMPLEMENTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtUnmapGraphicHandle(HSAuint32 NodeId,
HSAuint64 FlatMemoryAddress,
HSAuint64 SizeInBytes) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtGetTileConfig(HSAuint32 NodeId,
HsaGpuTileConfig *config) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtQueryPointerInfo(const void *Pointer,
HsaPointerInfo *PointerInfo) {
CHECK_DXG_OPEN();
if (!Pointer || !PointerInfo)
return HSAKMT_STATUS_INVALID_PARAMETER;
pr_debug("[%s] pointer %p\n", __func__, Pointer);
memset(PointerInfo, 0, sizeof(HsaPointerInfo));
Allocation allocation_info;
{
std::lock_guard<std::mutex> gard(allocation_map_lock_);
auto it = allocation_map_.find(Pointer);
if (it == allocation_map_.end()) {
PointerInfo->Type = HSA_POINTER_UNKNOWN;
return HSAKMT_STATUS_ERROR;
}
allocation_info = it->second;
}
if (allocation_info.userptr) {
PointerInfo->Type = HSA_POINTER_REGISTERED_USER;
PointerInfo->SizeInBytes = allocation_info.size;
} else {
PointerInfo->Type = HSA_POINTER_ALLOCATED;
PointerInfo->SizeInBytes = allocation_info.size_requested;
}
PointerInfo->Node = allocation_info.node_id;
PointerInfo->MemFlags.Value = allocation_info.mem_flags_value;
PointerInfo->CPUAddress = allocation_info.cpu_addr;
PointerInfo->GPUAddress = allocation_info.gpu_addr;
PointerInfo->UserData = allocation_info.user_data;
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtSetMemoryUserData(const void *Pointer,
void *UserData) {
CHECK_DXG_OPEN();
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtReplaceAsanHeaderPage(void *addr) {
assert(false);
#ifdef SANITIZER_AMDGPU
pr_debug("[%s] address %p\n", __func__, addr);
CHECK_DXG_OPEN();
return HSAKMT_STATUS_SUCCESS;
#else
return HSAKMT_STATUS_NOT_SUPPORTED;
#endif
}
HSAKMT_STATUS HSAKMTAPI hsaKmtReturnAsanHeaderPage(void *addr) {
assert(false);
#ifdef SANITIZER_AMDGPU
pr_debug("[%s] address %p\n", __func__, addr);
CHECK_DXG_OPEN();
return HSAKMT_STATUS_SUCCESS;
#else
return HSAKMT_STATUS_NOT_SUPPORTED;
#endif
}
+274
Parādīt failu
@@ -0,0 +1,274 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include <stdlib.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdio>
#include <strings.h>
#include <dlfcn.h>
#include <cassert>
#include <amdgpu.h>
#include "libhsakmt.h"
#include "inc/hsa/hsa.h"
#include "inc/hsa/hsa_ven_amd_loader.h"
int (*fn_amdgpu_device_get_fd)(HsaAMDGPUDeviceHandle device_handle);
hsa_signal_value_t (*fn_hsa_signal_load_relaxed)(hsa_signal_t signal);
hsa_signal_value_t (*fn_hsa_signal_wait_relaxed)(
hsa_signal_t signal, hsa_signal_condition_t condition,
hsa_signal_value_t compare_value, uint64_t timeout_hint,
hsa_wait_state_t wait_state_hint);
void (*fn_hsa_signal_store_screlease)(hsa_signal_t hsa_signal,
hsa_signal_value_t value);
hsa_status_t (*fn_hsa_ven_amd_loader_query_host_address)(
const void *device_address, const void **host_address);
static const char dxg_device_name[] = "/dev/dxg";
static pid_t parent_pid = -1;
int hsakmt_debug_level;
bool hsakmt_forked;
static int dxg_fd = -1;
/* is_forked_child detects when the process has forked since the last
* time this function was called. We cannot rely on pthread_atfork
* because the process can fork without calling the fork function in
* libc (using clone or calling the system call directly).
*/
bool is_forked_child(void) {
pid_t cur_pid;
if (hsakmt_forked)
return true;
cur_pid = getpid();
if (parent_pid == -1) {
parent_pid = cur_pid;
return false;
}
if (parent_pid != cur_pid) {
hsakmt_forked = true;
return true;
}
return false;
}
/* Callbacks from pthread_atfork */
static void prepare_fork_handler(void) { pthread_mutex_lock(&hsakmt_mutex); }
static void parent_fork_handler(void) { pthread_mutex_unlock(&hsakmt_mutex); }
static void child_fork_handler(void) {
pthread_mutex_init(&hsakmt_mutex, NULL);
hsakmt_forked = true;
}
/* Call this from the child process after fork. This will clear all
* data that is duplicated from the parent process, that is not valid
* in the child.
* The topology information is duplicated from the parent is valid
* in the child process so it is not cleared
*/
static void clear_after_fork(void) {
// TODO: fmm_clear_all_mem();
if (dxg_fd) {
close(dxg_fd);
dxg_fd = -1;
}
dxg_open_count = 0;
parent_pid = -1;
hsakmt_forked = false;
}
static inline void init_page_size(void) {
#ifndef PAGE_SIZE
PAGE_SIZE = sysconf(_SC_PAGESIZE);
#endif
PAGE_SHIFT = ffs(PAGE_SIZE) - 1;
}
static HSAKMT_STATUS init_vars_from_env(void) {
char *envvar;
int debug_level;
/* Normally libraries don't print messages. For debugging purpose, we'll
* print messages if an environment variable, HSAKMT_DEBUG_LEVEL, is set.
*/
hsakmt_debug_level = HSAKMT_DEBUG_LEVEL_DEFAULT;
envvar = getenv("HSAKMT_DEBUG_LEVEL");
if (envvar) {
debug_level = atoi(envvar);
if (debug_level >= HSAKMT_DEBUG_LEVEL_ERR &&
debug_level <= HSAKMT_DEBUG_LEVEL_DEBUG)
hsakmt_debug_level = debug_level;
}
/* Check whether to support Zero frame buffer */
envvar = getenv("HSA_ZFB");
if (envvar)
zfb_support = atoi(envvar);
/* Check whether to handle vendor specific aql packet */
envvar = getenv("WSLKMT_VENDOR_PACKET");
if (envvar)
vendor_packet_support = atoi(envvar);
return HSAKMT_STATUS_SUCCESS;
}
#define _HSAKMT_LOOKUP_SYMS(_sym) \
do { \
fn_##_sym = \
reinterpret_cast<decltype(fn_##_sym)>(dlsym(RTLD_DEFAULT, #_sym)); \
if (!fn_##_sym) { \
pr_err("%s not found - %s\n", #_sym, dlerror()); \
return HSAKMT_STATUS_ERROR; \
} \
} while (0)
static HSAKMT_STATUS init_symbols(void) {
_HSAKMT_LOOKUP_SYMS(hsa_signal_load_relaxed);
_HSAKMT_LOOKUP_SYMS(hsa_signal_wait_relaxed);
_HSAKMT_LOOKUP_SYMS(hsa_signal_store_screlease);
hsa_status_t (*fn_hsa_system_get_extension_table)(
uint16_t extension, uint16_t version_major, uint16_t version_minor,
void *table);
_HSAKMT_LOOKUP_SYMS(hsa_system_get_extension_table);
hsa_ven_amd_loader_1_03_pfn_t table;
fn_hsa_system_get_extension_table(HSA_EXTENSION_AMD_LOADER, 1, 3, &table);
fn_hsa_ven_amd_loader_query_host_address =
table.hsa_ven_amd_loader_query_host_address;
return HSAKMT_STATUS_SUCCESS;
}
static void load_libdrm_amdgpu(void) {
/* load libdrm_amdgpu */
int fd;
uint32_t major, minor;
amdgpu_device_handle device_handle;
amdgpu_device_initialize(fd, &major, &minor, &device_handle);
}
HSAKMT_STATUS HSAKMTAPI hsaKmtOpenKFD(void) {
HSAKMT_STATUS result;
int fd = -1;
HsaSystemProperties sys_props;
char *error;
char *useSvmStr;
pthread_mutex_lock(&hsakmt_mutex);
/* If the process has forked, the child process must re-initialize
* it's connection to DXG. Any references tracked by dxg_open_count
* belong to the parent
*/
if (is_forked_child())
clear_after_fork();
if (dxg_open_count == 0) {
static bool atfork_installed = false;
result = init_symbols();
if (result != HSAKMT_STATUS_SUCCESS)
goto open_failed;
load_libdrm_amdgpu();
result = init_vars_from_env();
if (result != HSAKMT_STATUS_SUCCESS)
goto open_failed;
if (dxg_fd < 0) {
fd = open(dxg_device_name, O_RDWR | O_CLOEXEC);
if (fd == -1) {
result = HSAKMT_STATUS_KERNEL_IO_CHANNEL_NOT_OPENED;
goto open_failed;
}
dxg_fd = fd;
}
init_page_size();
useSvmStr = getenv("HSA_USE_SVM");
is_svm_api_supported = !(useSvmStr && !strcmp(useSvmStr, "0")) && false;
// result = topology_sysfs_get_system_props(&sys_props);
if (result != HSAKMT_STATUS_SUCCESS)
goto topology_sysfs_failed;
dxg_open_count = 1;
if (!atfork_installed) {
/* Atfork handlers cannot be uninstalled and
* must be installed only once. Otherwise
* prepare will deadlock when trying to take
* the same lock multiple times.
*/
pthread_atfork(prepare_fork_handler, parent_fork_handler,
child_fork_handler);
atfork_installed = true;
}
} else {
dxg_open_count++;
result = HSAKMT_STATUS_KERNEL_ALREADY_OPENED;
}
pthread_mutex_unlock(&hsakmt_mutex);
return result;
topology_sysfs_failed:
close(fd);
open_failed:
pthread_mutex_unlock(&hsakmt_mutex);
return result;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtCloseKFD(void) {
HSAKMT_STATUS result;
pthread_mutex_lock(&hsakmt_mutex);
if (dxg_open_count > 0) {
if (--dxg_open_count == 0) {
close(dxg_fd);
}
result = HSAKMT_STATUS_SUCCESS;
} else
result = HSAKMT_STATUS_KERNEL_IO_CHANNEL_NOT_OPENED;
pthread_mutex_unlock(&hsakmt_mutex);
return result;
}
+73
Parādīt failu
@@ -0,0 +1,73 @@
/*
* Copyright © 2023 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "libhsakmt.h"
HSAKMT_STATUS HSAKMTAPI hsaKmtPcSamplingSupport(void) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI
hsaKmtPcSamplingQueryCapabilities(HSAuint32 NodeId, void *sample_info,
HSAuint32 sample_info_sz, HSAuint32 *size) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtPcSamplingCreate(HSAuint32 NodeId,
HsaPcSamplingInfo *sample_info,
HsaPcSamplingTraceId *traceId) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtPcSamplingDestroy(HSAuint32 NodeId,
HsaPcSamplingTraceId traceId) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtPcSamplingStart(HSAuint32 NodeId,
HsaPcSamplingTraceId traceId) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtPcSamplingStop(HSAuint32 NodeId,
HsaPcSamplingTraceId traceId) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
+82
Parādīt failu
@@ -0,0 +1,82 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include "libhsakmt.h"
HSAKMT_STATUS HSAKMTAPI hsaKmtPmcGetCounterProperties(
HSAuint32 NodeId, HsaCounterProperties **CounterProperties) {
return HSAKMT_STATUS_NOT_SUPPORTED;
}
/* Registers a set of (HW) counters to be used for tracing/profiling */
HSAKMT_STATUS HSAKMTAPI hsaKmtPmcRegisterTrace(HSAuint32 NodeId,
HSAuint32 NumberOfCounters,
HsaCounter *Counters,
HsaPmcTraceRoot *TraceRoot) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
/* Unregisters a set of (HW) counters used for tracing/profiling */
HSAKMT_STATUS HSAKMTAPI hsaKmtPmcUnregisterTrace(HSAuint32 NodeId,
HSATraceId TraceId) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtPmcAcquireTraceAccess(HSAuint32 NodeId,
HSATraceId TraceId) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtPmcReleaseTraceAccess(HSAuint32 NodeId,
HSATraceId TraceId) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
/* Starts tracing operation on a previously established set of performance
* counters */
HSAKMT_STATUS HSAKMTAPI hsaKmtPmcStartTrace(HSATraceId TraceId,
void *TraceBuffer,
HSAuint64 TraceBufferSizeBytes) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
/*Forces an update of all the counters that a previously started trace operation
* has registered */
HSAKMT_STATUS HSAKMTAPI hsaKmtPmcQueryTrace(HSATraceId TraceId) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
/* Stops tracing operation on a previously established set of performance
* counters */
HSAKMT_STATUS HSAKMTAPI hsaKmtPmcStopTrace(HSATraceId TraceId) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
+174
Parādīt failu
@@ -0,0 +1,174 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include <cinttypes>
#include "libhsakmt.h"
#include "inc/wddm/device.h"
#include "inc/wddm/queue.h"
#include "hsa-runtime/inc/amd_hsa_signal.h"
uint32_t get_vgpr_size_per_cu(HSA_ENGINE_ID id) {
uint32_t vgpr_size = 0x40000;
if (id.ui32.Major >= 11) {
vgpr_size = 0x60000;
}
return vgpr_size;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtCreateQueue(
HSAuint32 NodeId, HSA_QUEUE_TYPE Type, HSAuint32 QueuePercentage,
HSA_QUEUE_PRIORITY Priority, void *QueueAddress, HSAuint64 QueueSizeInBytes,
HsaEvent *Event, HsaQueueResource *QueueResource) {
HSAKMT_STATUS result;
CHECK_DXG_OPEN();
assert(Event == nullptr);
if (Priority < HSA_QUEUE_PRIORITY_MINIMUM ||
Priority > HSA_QUEUE_PRIORITY_MAXIMUM)
return HSAKMT_STATUS_INVALID_PARAMETER;
rocr::core::WDDMDevice *device_ = get_wddmdev(NodeId);
assert(device_);
switch (Type) {
case HSA_QUEUE_COMPUTE_AQL: {
assert(QueueResource->ErrorReason == nullptr);
uint64_t pkg_num = QueueSizeInBytes / 64;
uint32_t cmdbuf_size = device_->GetCmdbufSize();
uint32_t queue_engine = device_->GetComputeEngine();
bool use_hws = device_->IsHwsEnabled(queue_engine);
auto queue_ = new rocr::core::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->QueueId = reinterpret_cast<HSA_QUEUEID>(queue_);
// for doorbell_signal.hardware_doorbell_ptr
QueueResource->Queue_DoorBell_aql = queue_->GetDoorbellPtr();
} break;
case HSA_QUEUE_SDMA:
default:
assert(false);
QueueResource->QueueId = 0;
QueueResource->Queue_DoorBell = nullptr;
break;
}
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtUpdateQueue(
HSA_QUEUEID QueueId, HSAuint32 QueuePercentage, HSA_QUEUE_PRIORITY Priority,
void *QueueAddress, HSAuint64 QueueSize, HsaEvent *Event) {
CHECK_DXG_OPEN();
if (Priority < HSA_QUEUE_PRIORITY_MINIMUM ||
Priority > HSA_QUEUE_PRIORITY_MAXIMUM)
return HSAKMT_STATUS_INVALID_PARAMETER;
auto queue_ = reinterpret_cast<rocr::core::ComputeQueue *>(QueueId);
if (!queue_)
return HSAKMT_STATUS_INVALID_PARAMETER;
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtDestroyQueue(HSA_QUEUEID QueueId) {
CHECK_DXG_OPEN();
auto queue_ = reinterpret_cast<rocr::core::ComputeQueue *>(QueueId);
if (!queue_)
return HSAKMT_STATUS_INVALID_PARAMETER;
delete queue_;
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtSetQueueCUMask(HSA_QUEUEID QueueId,
HSAuint32 CUMaskCount,
HSAuint32 *QueueCUMask) {
CHECK_DXG_OPEN();
auto queue_ = reinterpret_cast<rocr::core::ComputeQueue *>(QueueId);
if (!queue_)
return HSAKMT_STATUS_INVALID_PARAMETER;
if (CUMaskCount == 0 || !QueueCUMask || ((CUMaskCount % 32) != 0))
return HSAKMT_STATUS_INVALID_PARAMETER;
pr_debug("%s not implemented\n", __func__);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtGetQueueInfo(HSA_QUEUEID QueueId,
HsaQueueInfo *QueueInfo) {
CHECK_DXG_OPEN();
if (QueueInfo == NULL)
return HSAKMT_STATUS_INVALID_PARAMETER;
memset(QueueInfo, 0, sizeof(*QueueInfo));
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtSetTrapHandler(HSAuint32 Node,
void *TrapHandlerBaseAddress,
HSAuint64 TrapHandlerSizeInBytes,
void *TrapBufferBaseAddress,
HSAuint64 TrapBufferSizeInBytes) {
CHECK_DXG_OPEN();
pr_debug("%s not implemented\n", __func__);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtAllocQueueGWS(HSA_QUEUEID QueueId, HSAuint32 nGWS,
HSAuint32 *firstGWS) {
CHECK_DXG_OPEN();
auto queue_ = reinterpret_cast<rocr::core::ComputeQueue *>(QueueId);
if (!queue_)
return HSAKMT_STATUS_INVALID_PARAMETER;
assert(false);
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtQueueRingDoorbell(HSA_QUEUEID QueueId) {
CHECK_DXG_OPEN();
auto queue_ = reinterpret_cast<rocr::core::ComputeQueue *>(QueueId);
if (!queue_)
return HSAKMT_STATUS_INVALID_PARAMETER;
queue_->RingDoorbell();
return HSAKMT_STATUS_SUCCESS;
}
Bināro failu nav iespējams attēlot.
+48
Parādīt failu
@@ -0,0 +1,48 @@
/*
* Copyright © 2020 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include "libhsakmt.h"
HSAKMT_STATUS HSAKMTAPI hsaKmtSPMAcquire(HSAuint32 PreferredNode) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtSPMSetDestBuffer(
HSAuint32 PreferredNode, HSAuint32 SizeInBytes, HSAuint32 *timeout,
HSAuint32 *SizeCopied, void *DestMemoryAddress, bool *isSPMDataLoss) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtSPMRelease(HSAuint32 PreferredNode) {
CHECK_DXG_OPEN();
// Used for profiling tools
return HSAKMT_STATUS_NOT_SUPPORTED;
}
+52
Parādīt failu
@@ -0,0 +1,52 @@
/*
* Copyright © 2020 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include "libhsakmt.h"
/* Helper functions for calling KFD SVM ioctl */
HSAKMT_STATUS HSAKMTAPI hsaKmtSVMSetAttr(void *start_addr, HSAuint64 size,
unsigned int nattr,
HSA_SVM_ATTRIBUTE *attrs) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtSVMGetAttr(void *start_addr, HSAuint64 size,
unsigned int nattr,
HSA_SVM_ATTRIBUTE *attrs) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtSetXNACKMode(HSAint32 enable) {
CHECK_DXG_OPEN();
return HSAKMT_STATUS_NOT_SUPPORTED;
}
HSAKMT_STATUS HSAKMTAPI hsaKmtGetXNACKMode(HSAint32 *enable) {
CHECK_DXG_OPEN();
*enable = false;
return HSAKMT_STATUS_SUCCESS;
}
+52
Parādīt failu
@@ -0,0 +1,52 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include <iostream>
#include <ctime>
#include <cstring>
#include <cassert>
#include "libhsakmt.h"
#include "inc/wddm/device.h"
HSAKMT_STATUS HSAKMTAPI hsaKmtGetClockCounters(HSAuint32 NodeId,
HsaClockCounters *Counters) {
HSAKMT_STATUS result = HSAKMT_STATUS_SUCCESS;
CHECK_DXG_OPEN();
std::memset(Counters, 0, sizeof(*Counters));
rocr::core::WDDMDevice *device_ = get_wddmdev(NodeId);
assert(device_);
device_->GetClockCounters(&Counters->GPUClockCounter, nullptr);
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) == 0)
Counters->CPUClockCounter = ts.tv_sec * 1e9 + ts.tv_nsec;
if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0)
Counters->SystemClockCounter = ts.tv_sec * 1e9 + ts.tv_nsec;
Counters->SystemClockFrequencyHz = 1000000000;
return result;
}
+1698
Parādīt failu
Failā izmaiņas netiks attēlotas, jo tās ir par lielu Ielādēt izmaiņas
+519
Parādīt failu
@@ -0,0 +1,519 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
/*
Helpers to use native types with C++11 atomic operations.
Fixes GCC builtin functionality for x86 with respect to WC and non-temporal
stores.
*/
#ifndef HSA_RUNTIME_CORE_UTIL_ATOMIC_HELPERS_H_
#define HSA_RUNTIME_CORE_UTIL_ATOMIC_HELPERS_H_
#include <atomic>
#include "utils.h"
//ALWAYS_CONSERVATIVE will very likely overfence your code.
//For use as a debugging aid only.
#define ALWAYS_CONSERVATIVE 0
#if !ALWAYS_CONSERVATIVE
#if defined(__x86_64__) || defined(_M_X64)
#define X64_ORDER_WC 1
#endif
#if X64_ORDER_WC
#include <xmmintrin.h>
#endif
#endif
namespace rocr {
namespace atomic {
static constexpr int c11ToBuiltInFlags(std::memory_order order)
{
#if ALWAYS_CONSERVATIVE
return __ATOMIC_RELAXED;
#elif X64_ORDER_WC
return __ATOMIC_RELAXED;
#else
return (order == std::memory_order_relaxed) ? __ATOMIC_RELAXED :
(order == std::memory_order_acquire) ? __ATOMIC_ACQUIRE :
(order == std::memory_order_release) ? __ATOMIC_RELEASE :
(order == std::memory_order_seq_cst) ? __ATOMIC_SEQ_CST :
(order == std::memory_order_consume) ? __ATOMIC_CONSUME :
(order == std::memory_order_acq_rel) ? __ATOMIC_ACQ_REL :
__ATOMIC_SEQ_CST;
#endif
}
static __forceinline void PreFence(std::memory_order order) {
#if ALWAYS_CONSERVATIVE
switch (order) {
case std::memory_order_release:
case std::memory_order_seq_cst:
case std::memory_order_acq_rel:
__atomic_thread_fence(__ATOMIC_SEQ_CST);
default:;
}
#elif X64_ORDER_WC
switch (order) {
case std::memory_order_release:
case std::memory_order_seq_cst:
case std::memory_order_acq_rel:
_mm_sfence();
default:;
}
#endif
}
static __forceinline void PostFence(std::memory_order order) {
#if ALWAYS_CONSERVATIVE
switch (order) {
case std::memory_order_seq_cst:
case std::memory_order_acq_rel:
case std::memory_order_acquire:
__atomic_thread_fence(__ATOMIC_SEQ_CST);
default:;
}
#elif X64_ORDER_WC
switch (order) {
case std::memory_order_seq_cst:
return _mm_mfence();
case std::memory_order_acq_rel:
case std::memory_order_acquire:
return _mm_lfence();
default:;
}
#endif
}
static __forceinline void Fence(std::memory_order order=std::memory_order_seq_cst) {
#if ALWAYS_CONSERVATIVE
__atomic_thread_fence(__ATOMIC_SEQ_CST);
#elif X64_ORDER_WC
switch (order) {
case std::memory_order_seq_cst:
case std::memory_order_acq_rel:
return _mm_mfence();
case std::memory_order_acquire:
return _mm_lfence();
case std::memory_order_release:
return _mm_sfence();
default:;
}
#else
std::atomic_thread_fence(order);
#endif
}
template <class T>
static __forceinline void BasicCheck(const T* ptr) {
constexpr bool value = __atomic_always_lock_free(sizeof(T), 0);
static_assert(value, "Atomic type may not be compatible with peripheral atomics.");
};
template <class T>
static __forceinline void BasicCheck(const volatile T* ptr) {
constexpr bool value = __atomic_always_lock_free(sizeof(T), 0);
static_assert(value, "Atomic type may not be compatible with peripheral atomics.");
};
/// @brief: Load value of type T atomically with specified memory order.
/// @param: ptr(Input), a pointer to type T.
/// @param: order(Input), memory order with atomic load, relaxed by default.
/// @return: T, loaded value.
template <class T>
static __forceinline T
Load(const T* ptr, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
T ret;
PreFence(order);
__atomic_load(ptr, &ret, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: function overloading, for more info, see previous one.
/// @param: ptr(Input), a pointer to volatile type T.
/// @param: order(Input), memory order with atomic load, relaxed by default.
/// @return: T, loaded value.
template <class T>
static __forceinline T
Load(const volatile T* ptr,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
T ret;
PreFence(order);
__atomic_load(ptr, &ret, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Store value of type T with specified memory order.
/// @param: ptr(Input), a pointer to instance which will be stored.
/// @param: val(Input), value to be stored.
/// @param: order(Input), memory order with atomic store, relaxed by default.
/// @return: void.
template <class T>
static __forceinline void Store(
T* ptr, T val, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
__atomic_store(ptr, &val, c11ToBuiltInFlags(order));
PostFence(order);
}
/// @brief: Function overloading, for more info, see previous one.
/// @param: ptr(Input), a pointer to volatile instance which will be stored.
/// @param: val(Input), value to be stored.
/// @param: order(Input), memory order with atomic store, relaxed by default.
/// @return: void.
template <class T>
static __forceinline void Store(
volatile T* ptr, T val,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
__atomic_store(ptr, &val, c11ToBuiltInFlags(order));
PostFence(order);
}
/// @brief: Compare and swap value atomically with specified memory order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: val(Input), value to be stored if condition is satisfied.
/// @param: expected(Input), value which is expected.
/// @param: order(Input), memory order with atomic operation.
/// @return: T, observed value of type T.
template <class T>
static __forceinline T
Cas(T* ptr, T val, T expected,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
__atomic_compare_exchange(ptr, &expected, &val, false, c11ToBuiltInFlags(order), __ATOMIC_RELAXED);
PostFence(order);
return expected;
}
/// @brief: Function overloading, for more info, see previous one.
/// @param: ptr(Input), a pointer to volatile variable which is operated on.
/// @param: val(Input), value to be stored if condition is satisfied.
/// @param: expected(Input), value which is expected.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, observed value of type T.
template <class T>
static __forceinline T
Cas(volatile T* ptr, T val, T expected,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
__atomic_compare_exchange(ptr, &expected, &val, false, c11ToBuiltInFlags(order), __ATOMIC_RELAXED);
PostFence(order);
return expected;
}
/// @brief: Exchange the value atomically with specified memory order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: val(Input), value to be stored.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, the value prior to the exchange.
template <class T>
static __forceinline T
Exchange(T* ptr, T val,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
T ret;
PreFence(order);
__atomic_exchange(ptr, &val, &ret, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Function overloading, for more info, see previous one.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: val(Input), value to be stored.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, the value prior to the exchange.
template <class T>
static __forceinline T
Exchange(volatile T* ptr, T val,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
T ret;
PreFence(order);
__atomic_exchange(ptr, &val, &ret, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Add value to variable atomically with specified memory order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: val(Input), value to be added.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, the value of the variable prior to the addition.
template <class T>
static __forceinline T
Add(T* ptr, T val, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_add(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Subtract value from the variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: val(Input), value to be subtraced.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of the variable prior to the subtraction.
template <class T>
static __forceinline T
Sub(T* ptr, T val, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_sub(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Bit And operation on variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: val(Input), value which is ANDed with variable.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of variable prior to the operation.
template <class T>
static __forceinline T
And(T* ptr, T val, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_and(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Bit Or operation on variable atomically with specified memory order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: val(Input), value which is ORed with variable.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of variable prior to the operation.
template <class T>
static __forceinline T
Or(T* ptr, T val, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_or(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Bit Xor operation on variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: val(Input), value which is XORed with variable.
/// @order: order(Input), memory order which is relaxed by default.
/// @return: T, valud of variable prior to the opertaion.
template <class T>
static __forceinline T
Xor(T* ptr, T val, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_xor(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Increase the value of variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of variable prior to the operation.
template <class T>
static __forceinline T
Increment(T* ptr, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_add(ptr, 1, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Decrease the value of the variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to variable which is operated on.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of variable prior to the operation.
template <class T>
static __forceinline T
Decrement(T* ptr, std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_sub(ptr, 1, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Add value to variable atomically with specified memory order.
/// @param: ptr(Input), a pointer to volatile variable which is operated on.
/// @param: val(Input), value to be added.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, the value of the variable prior to the addition.
template <class T>
static __forceinline T
Add(volatile T* ptr, T val,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_add(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Subtract value from the variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to volatile variable which is operated on.
/// @param: val(Input), value to be subtraced.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of the variable prior to the subtraction.
template <class T>
static __forceinline T
Sub(volatile T* ptr, T val,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_sub(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Bit And operation on variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to volatile variable which is operated on.
/// @param: val(Input), value which is ANDed with variable.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of variable prior to the operation.
template <class T>
static __forceinline T
And(volatile T* ptr, T val,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_and(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Bit Or operation on variable atomically with specified memory order.
/// @param: ptr(Input), a pointer to volatile variable which is operated on.
/// @param: val(Input), value which is ORed with variable.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of variable prior to the operation.
template <class T>
static __forceinline T Or(volatile T* ptr, T val,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_or(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Bit Xor operation on variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to volatile variable which is operated on.
/// @param: val(Input), value which is XORed with variable.
/// @order: order(Input), memory order which is relaxed by default.
/// @return: T, valud of variable prior to the opertaion.
template <class T>
static __forceinline T
Xor(volatile T* ptr, T val,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_xor(ptr, val, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Increase the value of variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to volatile variable which is operated on.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of variable prior to the operation.
template <class T>
static __forceinline T
Increment(volatile T* ptr,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_add(ptr, 1, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
/// @brief: Decrease the value of the variable atomically with specified memory
/// order.
/// @param: ptr(Input), a pointer to volatile variable which is operated on.
/// @param: order(Input), memory order which is relaxed by default.
/// @return: T, value of variable prior to the operation.
template <class T>
static __forceinline T
Decrement(volatile T* ptr,
std::memory_order order = std::memory_order_relaxed) {
BasicCheck<T>(ptr);
PreFence(order);
T ret = __atomic_fetch_sub(ptr, 1, c11ToBuiltInFlags(order));
PostFence(order);
return ret;
}
} // namespace atomic
} // namespace rocr
#ifdef X64_ORDER_WC
#undef X64_ORDER_WC
#endif
#ifdef ALWAYS_CONSERVATIVE
#undef ALWAYS_CONSERVATIVE
#endif
#endif // HSA_RUNTIME_CORE_UTIL_ATOMIC_HELPERS_H_
+226
Parādīt failu
@@ -0,0 +1,226 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2021-2024, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIESd OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#include "core/util/flag.h"
#include "core/util/utils.h"
#include "core/util/os.h"
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <locale>
namespace rocr {
FILE* log_file = stderr;
uint8_t log_flags[8];
void log_printf(const char* file, int line, const char* format, ...) {
va_list ap;
std::stringstream str_thrd_id;
str_thrd_id << std::hex << std::this_thread::get_id();
va_start(ap, format);
char message[4096];
vsnprintf(message, sizeof(message), format, ap);
va_end(ap);
fprintf(log_file, ":%-25s:%-4d: %010lld us: [pid:%-5d tid:0x%s] [***rocr***] %s\n",
file, line, os::ReadAccurateClock()/1000ULL, os::GetProcessId(),
str_thrd_id.str().c_str(), message);
fflush(log_file);
}
// split at separators
static std::vector<std::string> split(std::string& str, char sep) {
std::vector<std::string> ret;
while (!str.empty()) {
size_t pos = str.find(sep);
if (pos == std::string::npos) {
ret.push_back(str);
return ret;
}
ret.push_back(str.substr(0, pos));
str.erase(0, pos + 1);
}
return ret;
};
// Parse id,id-id,... strings into id lists
static std::vector<uint32_t> get_elements(std::string& str, uint32_t maxElement) {
std::vector<uint32_t> ret;
MAKE_NAMED_SCOPE_GUARD(error, [&]() { ret.clear(); });
std::vector<std::string> ranges = split(str, ',');
for (auto& str : ranges) {
auto range = split(str, '-');
// failure, too many -'s.
if (range.size() > 2) return ret;
char* end;
uint32_t index = strtoul(range[0].c_str(), &end, 10);
// Invalid syntax - id's must be base 10 digits only.
if (*end != '\0') return ret;
if (index <= maxElement) ret.push_back(index);
if (range.size() == 2) {
uint32_t secondindex = strtoul(range[1].c_str(), &end, 10);
if (*end != '\0') return ret; // bad syntax
if (secondindex < index) return ret; // inverted range
secondindex = Min(secondindex, maxElement);
for (uint32_t i = index + 1; i < secondindex + 1; i++) ret.push_back(i);
}
}
// Confirm no duplicate ids.
std::sort(ret.begin(), ret.end());
if (std::adjacent_find(ret.begin(), ret.end()) != ret.end()) return ret;
// Good parse, keep result.
error.Dismiss();
return ret;
};
/*
Parse env var per the following syntax, all whitespace is ignored:
ID = [0-9][0-9]* ex. base 10 numbers
ID_list = (ID | ID-ID)[, (ID | ID-ID)]* ex. 0,2-4,7
GPU_list = ID_list ex. 0,2-4,7
CU_list = 0x[0-F]* | ID_list ex. 0x337F OR 0,2-4,7
CU_Set = GPU_list : CU_list ex. 0,2-4,7:0-15,32-47 OR 0,2-4,7:0x337F
HSA_CU_MASK = CU_Set [; CU_Set]* ex. 0,2-4,7:0-15,32-47; 3-9:0x337F
GPU indexes are taken post ROCR_VISIBLE_DEVICES reordering.
Listed or bit set CUs will be enabled at queue creation on the associated GPU.
All other CUs on the associated GPUs will be disabled.
CU masks of unlisted GPUs are not restricted.
Repeating a GPU or CU ID is a syntax error.
Parsing stops at the first CU_Set that has a syntax error, that set and all
following sets are ignored.
Specifying a mask with no usable CUs (CU_list is 0x0) is a syntax error.
Users should use ROCR_VISIBLE_DEVICES if they want to exclude use of a
particular GPU.
*/
void Flag::parse_masks(std::string& var, uint32_t maxGpu, uint32_t maxCU) {
if (var.empty()) return;
// Remove whitespace
auto end = std::remove_if(var.begin(), var.end(),
[](char c) { return std::isspace<char>(c, std::locale::classic()); });
var.erase(end, var.end());
// Switch to uppercase
for (auto& c : var) c = toupper(c);
// Iterate over cu sets
auto sets = split(var, ';');
for (auto& set : sets) {
auto parts = split(set, ':');
if (parts.size() != 2) return;
// temp storage for cu_set parsing.
std::vector<uint32_t> gpu_index;
std::vector<uint32_t> mask;
// parse cu list first, check for bitmask format
if (parts[1][1] == 'X') {
// Confirm hex format and strip prefix
auto& cu = parts[1];
if (cu[0] != '0') return;
cu.erase(0, 2);
// Ensure all valid hex characters
for (auto& c : cu) {
if (!isxdigit(c)) return;
}
// Convert to uint32_t, lsb first.
size_t len = cu.length();
while (len != 0) {
size_t trim = Min(len, size_t(8));
len -= trim;
auto tmp = cu.substr(len, trim);
auto chunk = stoul(tmp, nullptr, 16);
mask.push_back(chunk);
}
// Trim dwords beyond maxCUs
uint32_t maxDwords = maxCU / 32 + 1;
if (maxDwords < mask.size()) mask.resize(maxDwords);
// Trim leading zeros
while (!mask.empty() && mask.back() == 0) mask.pop_back();
// Mask 0x0 is an error.
if (mask.empty()) return;
} else {
// parse cu lists
auto cu_indices = get_elements(parts[1], maxCU);
if (cu_indices.empty()) return;
uint32_t maxdword = cu_indices.back() / 32 + 1;
mask.resize(maxdword, 0);
for (auto id : cu_indices) {
uint32_t index, offset;
index = id / 32;
offset = id % 32;
mask[index] |= 1ul << offset;
}
}
// parse device list
gpu_index = get_elements(parts[0], maxGpu);
if (gpu_index.empty()) return;
// Ensure that no GPU was repeated across cu_sets
for (auto id : gpu_index) {
if (cu_mask_.find(id) != cu_mask_.end()) return;
}
// Insert into map
for (auto id : gpu_index) {
cu_mask_[id] = mask;
}
}
}
} // namespace rocr
+360
Parādīt failu
@@ -0,0 +1,360 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2021, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIESd OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef HSA_RUNTIME_CORE_INC_FLAG_H_
#define HSA_RUNTIME_CORE_INC_FLAG_H_
#include <stdint.h>
#include <vector>
#include <map>
#include <string>
#include "core/util/os.h"
#include "core/util/utils.h"
namespace rocr {
class Flag {
public:
enum SDMA_OVERRIDE { SDMA_DISABLE, SDMA_ENABLE, SDMA_DEFAULT };
enum SRAMECC_ENABLE { SRAMECC_DISABLED, SRAMECC_ENABLED, SRAMECC_DEFAULT };
// The values are meaningful and chosen to satisfy the thunk API.
enum XNACK_REQUEST { XNACK_DISABLE = 0, XNACK_ENABLE = 1, XNACK_UNCHANGED = 2 };
static_assert(XNACK_DISABLE == 0, "XNACK_REQUEST enum values improperly changed.");
static_assert(XNACK_ENABLE == 1, "XNACK_REQUEST enum values improperly changed.");
// Lift limit for 2.10 release RCCL workaround.
const size_t DEFAULT_SCRATCH_SINGLE_LIMIT = 146800640; // small_limit >> 2;
explicit Flag() { Refresh(); }
virtual ~Flag() {}
void Refresh() {
std::string var = os::GetEnvVar("HSA_CHECK_FLAT_SCRATCH");
check_flat_scratch_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_ENABLE_VM_FAULT_MESSAGE");
enable_vm_fault_message_ = (var == "0") ? false : true;
var = os::GetEnvVar("HSA_ENABLE_QUEUE_FAULT_MESSAGE");
enable_queue_fault_message_ = (var == "0") ? false : true;
var = os::GetEnvVar("HSA_ENABLE_INTERRUPT");
enable_interrupt_ = (var == "0") ? false : true;
var = os::GetEnvVar("HSA_ENABLE_SDMA");
enable_sdma_ = (var == "0") ? SDMA_DISABLE : ((var == "1") ? SDMA_ENABLE : SDMA_DEFAULT);
var = os::GetEnvVar("HSA_ENABLE_PEER_SDMA");
enable_peer_sdma_ = (var == "0") ? SDMA_DISABLE : ((var == "1") ? SDMA_ENABLE : SDMA_DEFAULT);
var = os::GetEnvVar("HSA_ENABLE_SDMA_GANG");
enable_sdma_gang_ = (var == "0") ? SDMA_DISABLE :
((var == "1") ? SDMA_ENABLE : SDMA_DEFAULT);
var = os::GetEnvVar("HSA_ENABLE_SDMA_COPY_SIZE_OVERRIDE");
enable_sdma_copy_size_override_ = (var == "0") ? SDMA_DISABLE :
((var == "1") ? SDMA_ENABLE : SDMA_DEFAULT);
visible_gpus_ = os::GetEnvVar("ROCR_VISIBLE_DEVICES");
filter_visible_gpus_ = os::IsEnvVarSet("ROCR_VISIBLE_DEVICES");
var = os::GetEnvVar("HSA_RUNNING_UNDER_VALGRIND");
running_valgrind_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_SDMA_WAIT_IDLE");
sdma_wait_idle_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_MAX_QUEUES");
max_queues_ = static_cast<uint32_t>(atoi(var.c_str()));
// Maximum amount of scratch mem that can be used per process per gpu
var = os::GetEnvVar("HSA_SCRATCH_MEM");
scratch_mem_size_ = atoi(var.c_str());
// Scratch memory sizes > HSA_SCRATCH_SINGLE_LIMIT will trigger a use-once scheme
// We also reserve HSA_SCRATCH_SINGLE_LIMIT per process per gpu to guarrantee we
// have sufficient memory to for scratch in case user tried to allocate all device
// memory
if (os::IsEnvVarSet("HSA_SCRATCH_SINGLE_LIMIT")) {
var = os::GetEnvVar("HSA_SCRATCH_SINGLE_LIMIT");
scratch_single_limit_ = atoi(var.c_str());
} else {
scratch_single_limit_ = DEFAULT_SCRATCH_SINGLE_LIMIT;
}
tools_lib_names_ = os::GetEnvVar("HSA_TOOLS_LIB");
var = os::GetEnvVar("HSA_TOOLS_REPORT_LOAD_FAILURE");
ifdebug {
report_tool_load_failures_ = (var == "1") ? true : false;
} else {
report_tool_load_failures_ = (var == "0") ? false : true;
}
var = os::GetEnvVar("HSA_DISABLE_FRAGMENT_ALLOCATOR");
disable_fragment_alloc_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_ENABLE_SDMA_HDP_FLUSH");
enable_sdma_hdp_flush_ = (var == "0") ? false : true;
var = os::GetEnvVar("HSA_REV_COPY_DIR");
rev_copy_dir_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_FORCE_FINE_GRAIN_PCIE");
fine_grain_pcie_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_NO_SCRATCH_RECLAIM");
no_scratch_reclaim_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_NO_SCRATCH_THREAD_LIMITER");
no_scratch_thread_limit_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_DISABLE_IMAGE");
disable_image_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_DISABLE_PC_SAMPLING");
disable_pc_sampling_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_LOADER_ENABLE_MMAP_URI");
loader_enable_mmap_uri_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_FORCE_SDMA_SIZE");
force_sdma_size_ = var.empty() ? 1024 * 1024 : atoi(var.c_str());
var = os::GetEnvVar("HSA_IGNORE_SRAMECC_MISREPORT");
check_sramecc_validity_ = (var == "1") ? false : true;
// Legal values are zero "0" or one "1". Any other value will
// be interpreted as not defining the env variable.
var = os::GetEnvVar("HSA_XNACK");
xnack_ = (var == "0") ? XNACK_DISABLE : ((var == "1") ? XNACK_ENABLE : XNACK_UNCHANGED);
var = os::GetEnvVar("HSA_ENABLE_DEBUG");
debug_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_CU_MASK_SKIP_INIT");
cu_mask_skip_init_ = (var == "1") ? true : false;
// Temporary opt-in for corrected HSA_AMD_AGENT_INFO_COOPERATIVE_COMPUTE_UNIT_COUNT behavior.
// Will become opt-out and possibly removed in future releases.
var = os::GetEnvVar("HSA_COOP_CU_COUNT");
coop_cu_count_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_DISCOVER_COPY_AGENTS");
discover_copy_agents_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_SVM_PROFILE");
svm_profile_ = var;
var = os::GetEnvVar("HSA_ENABLE_SRAMECC");
sramecc_enable_ =
(var == "0") ? SRAMECC_DISABLED : ((var == "1") ? SRAMECC_ENABLED : SRAMECC_DEFAULT);
var = os::GetEnvVar("HSA_IMAGE_PRINT_SRD");
image_print_srd_ = (var == "1") ? true : false;
var = os::GetEnvVar("HSA_ENABLE_MWAITX");
enable_mwaitx_ = (var == "1") ? true : false;
// Temporary environment variable to disable CPU affinity override
// Will either rename to HSA_OVERRIDE_CPU_AFFINITY later or remove completely.
var = os::GetEnvVar("HSA_OVERRIDE_CPU_AFFINITY_DEBUG");
override_cpu_affinity_ = (var == "0") ? false : true;
}
void parse_masks(uint32_t maxGpu, uint32_t maxCU) {
std::string var = os::GetEnvVar("HSA_CU_MASK");
parse_masks(var, maxGpu, maxCU);
}
bool check_flat_scratch() const { return check_flat_scratch_; }
bool enable_vm_fault_message() const { return enable_vm_fault_message_; }
bool enable_queue_fault_message() const { return enable_queue_fault_message_; }
bool enable_interrupt() const { return enable_interrupt_; }
bool enable_sdma_hdp_flush() const { return enable_sdma_hdp_flush_; }
bool running_valgrind() const { return running_valgrind_; }
bool sdma_wait_idle() const { return sdma_wait_idle_; }
bool report_tool_load_failures() const { return report_tool_load_failures_; }
bool disable_fragment_alloc() const { return disable_fragment_alloc_; }
bool rev_copy_dir() const { return rev_copy_dir_; }
bool fine_grain_pcie() const { return fine_grain_pcie_; }
bool no_scratch_reclaim() const { return no_scratch_reclaim_; }
bool no_scratch_thread_limiter() const { return no_scratch_thread_limit_; }
SDMA_OVERRIDE enable_sdma() const { return enable_sdma_; }
SDMA_OVERRIDE enable_peer_sdma() const { return enable_peer_sdma_; }
SDMA_OVERRIDE enable_sdma_gang() const { return enable_sdma_gang_; }
SDMA_OVERRIDE enable_sdma_copy_size_override() const { return enable_sdma_copy_size_override_; }
std::string visible_gpus() const { return visible_gpus_; }
bool filter_visible_gpus() const { return filter_visible_gpus_; }
uint32_t max_queues() const { return max_queues_; }
size_t scratch_mem_size() const { return scratch_mem_size_; }
size_t scratch_single_limit() const { return scratch_single_limit_; }
std::string tools_lib_names() const { return tools_lib_names_; }
bool disable_image() const { return disable_image_; }
bool disable_pc_sampling() const { return disable_pc_sampling_; }
bool loader_enable_mmap_uri() const { return loader_enable_mmap_uri_; }
size_t force_sdma_size() const { return force_sdma_size_; }
bool check_sramecc_validity() const { return check_sramecc_validity_; }
bool override_cpu_affinity() const { return override_cpu_affinity_; }
bool image_print_srd() const { return image_print_srd_; }
bool check_mwaitx(bool mwaitx_supported) {
if (enable_mwaitx_ && !mwaitx_supported) enable_mwaitx_ = false;
return enable_mwaitx_;
}
XNACK_REQUEST xnack() const { return xnack_; }
bool debug() const { return debug_; }
const std::vector<uint32_t>& cu_mask(uint32_t gpu_index) const {
static const std::vector<uint32_t> empty;
auto it = cu_mask_.find(gpu_index);
if (it == cu_mask_.end()) return empty;
return it->second;
}
bool cu_mask_skip_init() const { return cu_mask_skip_init_; }
bool coop_cu_count() const { return coop_cu_count_; }
bool discover_copy_agents() const { return discover_copy_agents_; }
const std::string& svm_profile() const { return svm_profile_; }
SRAMECC_ENABLE sramecc_enable() const { return sramecc_enable_; }
private:
bool check_flat_scratch_;
bool enable_vm_fault_message_;
bool enable_interrupt_;
bool enable_sdma_hdp_flush_;
bool running_valgrind_;
bool sdma_wait_idle_;
bool enable_queue_fault_message_;
bool report_tool_load_failures_;
bool disable_fragment_alloc_;
bool rev_copy_dir_;
bool fine_grain_pcie_;
bool no_scratch_reclaim_;
bool no_scratch_thread_limit_;
bool disable_image_;
bool disable_pc_sampling_;
bool loader_enable_mmap_uri_;
bool check_sramecc_validity_;
bool debug_;
bool cu_mask_skip_init_;
bool coop_cu_count_;
bool discover_copy_agents_;
bool override_cpu_affinity_;
bool image_print_srd_;
bool enable_mwaitx_;
SDMA_OVERRIDE enable_sdma_;
SDMA_OVERRIDE enable_peer_sdma_;
SDMA_OVERRIDE enable_sdma_gang_;
SDMA_OVERRIDE enable_sdma_copy_size_override_;
bool filter_visible_gpus_;
std::string visible_gpus_;
uint32_t max_queues_;
size_t scratch_mem_size_;
size_t scratch_single_limit_;
std::string tools_lib_names_;
std::string svm_profile_;
size_t force_sdma_size_;
// Indicates user preference for Xnack state.
XNACK_REQUEST xnack_;
SRAMECC_ENABLE sramecc_enable_;
// Map GPU index post RVD to its default cu mask.
std::map<uint32_t, std::vector<uint32_t>> cu_mask_;
void parse_masks(std::string& args, uint32_t maxGpu, uint32_t maxCU);
DISALLOW_COPY_AND_ASSIGN(Flag);
};
} // namespace rocr
#endif // header guard
+155
Parādīt failu
@@ -0,0 +1,155 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIESd OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef HSA_RUNTIME_CORE_UTIL_LAZY_PTR_H_
#define HSA_RUNTIME_CORE_UTIL_LAZY_PTR_H_
#include <memory>
#include <utility>
#include <functional>
#include "core/util/locks.h"
#include "core/util/utils.h"
namespace rocr {
/*
* Wrapper for a std::unique_ptr that initializes its object at first use.
*/
template <typename T> class lazy_ptr {
public:
lazy_ptr() {}
explicit lazy_ptr(std::function<T*()> Constructor) { reset(Constructor); }
lazy_ptr(lazy_ptr&& rhs) {
obj = std::move(rhs.obj);
func = std::move(rhs.func);
}
lazy_ptr& operator=(lazy_ptr&& rhs) {
obj = std::move(rhs.obj);
func = std::move(rhs.func);
}
lazy_ptr(lazy_ptr&) = delete;
lazy_ptr& operator=(lazy_ptr&) = delete;
void reset(std::function<T*()> Constructor = nullptr) {
obj.reset();
func = Constructor;
}
void reset(T* ptr) {
obj.reset(ptr);
func = nullptr;
}
bool operator==(T* rhs) const { return obj.get() == rhs; }
bool operator!=(T* rhs) const { return obj.get() != rhs; }
const std::unique_ptr<T>& operator->() const {
make(true);
assert(obj != nullptr && "Null dereference through lazy_ptr.");
return obj;
}
std::unique_ptr<T>& operator*() {
make(true);
return obj;
}
const std::unique_ptr<T>& operator*() const {
make(true);
return obj;
}
/*
* Ensures that the object is created or is being created.
* This is useful when early construction of the object is required.
*/
void touch() const { make(false); }
// Tells if the lazy object has been constructed or not.
// Construction may fail silently (return nullptr).
bool created() const {
std::atomic_thread_fence(std::memory_order_acquire);
return func == nullptr;
}
// Tells if the lazy object exists or not.
bool empty() const {
std::atomic_thread_fence(std::memory_order_acquire);
return obj == nullptr;
}
private:
mutable std::unique_ptr<T> obj;
mutable std::function<T*(void)> func;
mutable KernelMutex lock;
// Separated from make to improve inlining.
void make_body(bool block) const {
if (block) {
lock.Acquire();
} else if (!lock.Try()) {
return;
}
MAKE_SCOPE_GUARD([&]() { lock.Release(); });
if (func == nullptr) return;
T* ptr = func();
obj.reset(ptr);
std::atomic_thread_fence(std::memory_order_release);
func = nullptr;
}
__forceinline void make(bool block) const {
if (!created()) {
make_body(block);
}
}
};
} // namespace rocr
#endif // HSA_RUNTIME_CORE_UTIL_LAZY_PTR_H_
+771
Parādīt failu
@@ -0,0 +1,771 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2024, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifdef __linux__
#include "core/util/os.h"
#include "core/util/utils.h"
#include <link.h>
#include <dlfcn.h>
#include <pthread.h>
#include <limits.h>
#include <sched.h>
#include <sys/sysinfo.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <errno.h>
#include <cstring>
#include <atomic>
#include <memory>
#include <string>
#include <utility>
#include <semaphore.h>
#include "core/inc/runtime.h"
#if defined(__i386__) || defined(__x86_64__)
#include <cpuid.h>
#endif
namespace rocr {
namespace os {
struct ThreadArgs {
void* entry_args;
ThreadEntry entry_function;
};
void* __stdcall ThreadTrampoline(void* arg) {
ThreadArgs* ar = (ThreadArgs*)arg;
ThreadEntry CallMe = ar->entry_function;
void* Data = ar->entry_args;
delete ar;
CallMe(Data);
return nullptr;
}
// Thread container allows multiple waits and separate close (destroy).
class os_thread {
public:
explicit os_thread(ThreadEntry function, void* threadArgument, uint stackSize)
: thread(0), lock(nullptr), state(RUNNING) {
int err;
std::unique_ptr<ThreadArgs> args(new ThreadArgs);
lock = CreateMutex();
if (lock == nullptr) return;
args->entry_args = threadArgument;
args->entry_function = function;
pthread_attr_t attrib;
err = pthread_attr_init(&attrib);
if (err != 0) {
fprintf(stderr, "pthread_attr_init failed: %s\n", strerror(err));
return;
}
if (stackSize != 0) {
stackSize = Max(uint(PTHREAD_STACK_MIN), stackSize);
stackSize = AlignUp(stackSize, 4096);
err = pthread_attr_setstacksize(&attrib, stackSize);
if (err != 0) {
fprintf(stderr, "pthread_attr_setstacksize failed: %s\n", strerror(err));
err = pthread_attr_destroy(&attrib);
if (err != 0) {
fprintf(stderr, "pthread_attr_destroy failed: %s\n", strerror(err));
return;
}
}
}
int cores = 0;
cpu_set_t* cpuset = nullptr;
if (core::Runtime::runtime_singleton_->flag().override_cpu_affinity()) {
cores = get_nprocs_conf();
cpuset = CPU_ALLOC(cores);
if (cpuset == nullptr) {
fprintf(stderr, "CPU_ALLOC failed: %s\n", strerror(errno));
return;
}
CPU_ZERO_S(CPU_ALLOC_SIZE(cores), cpuset);
for (int i = 0; i < cores; i++) {
CPU_SET_S(i, CPU_ALLOC_SIZE(cores), cpuset);
}
err = pthread_attr_setaffinity_np(&attrib, CPU_ALLOC_SIZE(cores), cpuset);
CPU_FREE(cpuset);
if (err != 0) {
fprintf(stderr, "pthread_setaffinity_np failed: %s\n", strerror(err));
return;
}
}
err = pthread_create(&thread, &attrib, ThreadTrampoline, args.get());
// Probably a stack size error since system limits can be different from PTHREAD_STACK_MIN
// Attempt to grow the stack within reason.
if ((err == EINVAL) && stackSize != 0) {
while (stackSize < 20 * 1024 * 1024) {
stackSize *= 2;
err = pthread_attr_setstacksize(&attrib, stackSize);
if (err != 0) {
fprintf(stderr, "pthread_attr_setstacksize failed: %s\n", strerror(err));
return;
}
err = pthread_create(&thread, &attrib, ThreadTrampoline, args.get());
if (err != EINVAL) break;
debug_print("pthread_create returned EINVAL, doubling stack size\n");
}
}
if (err == 0)
args.release();
else
thread = 0;
err = pthread_attr_destroy(&attrib);
if (err != 0) {
fprintf(stderr, "pthread_attr_destroy failed: %s\n", strerror(err));
}
}
os_thread(os_thread&& rhs) {
thread = rhs.thread;
lock = rhs.lock;
state = int(rhs.state);
rhs.thread = 0;
rhs.lock = nullptr;
}
os_thread(os_thread&) = delete;
~os_thread() {
if (lock != nullptr) DestroyMutex(lock);
if ((state == RUNNING) && (thread != 0)) {
int err = pthread_detach(thread);
if (err != 0) fprintf(stderr, "pthread_detach failed: %s\n", strerror(err));
}
}
bool Valid() { return (lock != nullptr) && (thread != 0); }
bool Wait() {
if (state == FINISHED) return true;
AcquireMutex(lock);
if (state == FINISHED) {
ReleaseMutex(lock);
return true;
}
int err = pthread_join(thread, NULL);
bool success = (err == 0);
if (success) state = FINISHED;
ReleaseMutex(lock);
return success;
}
private:
pthread_t thread;
Mutex lock;
std::atomic<int> state;
enum { FINISHED = 0, RUNNING = 1 };
};
static_assert(sizeof(LibHandle) == sizeof(void*), "OS abstraction size mismatch");
static_assert(sizeof(Semaphore) == sizeof(sem_t*), "OS abstraction size mismatch");
static_assert(sizeof(Mutex) == sizeof(pthread_mutex_t*), "OS abstraction size mismatch");
static_assert(sizeof(SharedMutex) == sizeof(pthread_rwlock_t*), "OS abstraction size mismatch");
static_assert(sizeof(Thread) == sizeof(os_thread*), "OS abstraction size mismatch");
LibHandle LoadLib(std::string filename) {
void* ret = dlopen(filename.c_str(), RTLD_LAZY);
if (ret == nullptr) debug_print("LoadLib(%s) failed: %s\n", filename.c_str(), dlerror());
return *(LibHandle*)&ret;
}
void* GetExportAddress(LibHandle lib, std::string export_name) {
void* ret = dlsym(*(void**)&lib, export_name.c_str());
// dlsym searches the given library and all the library's load dependencies.
// Remaining code limits symbol lookup to only the library handle given.
// This lookup pattern matches Windows.
if (ret == NULL) return ret;
link_map* map;
int err = dlinfo(*(void**)&lib, RTLD_DI_LINKMAP, &map);
if (err == -1) {
fprintf(stderr, "dlinfo failed: %s\n", dlerror());
return nullptr;
}
Dl_info info;
err = dladdr(ret, &info);
if (err == 0) {
fprintf(stderr, "dladdr failed.\n");
return nullptr;
}
if (strcmp(info.dli_fname, map->l_name) == 0) return ret;
return NULL;
}
void CloseLib(LibHandle lib) { dlclose(*(void**)&lib); }
/*
* @brief Look for a symbol called "HSA_AMD_TOOL_PRIORITY" across all loaded
* shared libraries, and if found, store the name of the library
*
* @param[in]: info A dl_phdr_info struct pointer, which contains information
* about library's load address, header, and name.
*
* @param[in]: size integer size of dl_phdr_info struct
*
* @param[out]: data copy of the data argument to dl_phdr_iterate call
*
* @retval:: Return 0 on Success. If callback returns a non-zero value,
* dl_iterate_phdr() will stop processing, even if there are unprocessed
* shared objects.
*/
static int callback(struct dl_phdr_info* info, size_t size, void* data) {
std::vector<std::string>* loadedToolsLib = (std::vector<std::string>*)data;
assert(loadedToolsLib != nullptr);
/*
* Check if lib name is not empty and its not a "vdso.so" lib,
* The vDSO is a special shared object file that is built into the Linux kernel.
* It is not a regular shared library and thus does not have all the properties
* of regular shared libraries. The way the vDSO is loaded and organized in memory
* is different from regular shared libraries and it's not guaranteed that it
* will have a specific segment or section. Hence its skipped.
*/
if ((info) && (info->dlpi_name[0] != '\0')) {
if (std::string(info->dlpi_name).find("vdso.so") != std::string::npos) return 0;
/*
* Iterate through the program headers of the loaded lib and check for PT_DYNAMIC program
* header. If the PT_DYNAMIC program header is found, use dlpi_addr and dlpi_phdr members
* of dl_phdr_info struct to get the address of the dynamic section of the loaded
* library in memory
*/
for (int i = 0; i < info->dlpi_phnum; i++) {
if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
Elf64_Dyn* dyn_section = (Elf64_Dyn*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
char* strings = nullptr;
Elf64_Xword limit = 0;
/*
* The dynamic section is searched for DT_STRTAB (address of string table),
* and DT_STRSZ (size of string table)
* DT_NULL - Marks the end of the _DYNAMIC array
*/
for (int j = 0;; j++) {
if (dyn_section[j].d_tag == DT_NULL) break;
if (dyn_section[j].d_tag == DT_STRTAB) strings = (char*)(dyn_section[j].d_un.d_ptr);
if (dyn_section[j].d_tag == DT_STRSZ) limit = dyn_section[j].d_un.d_val;
}
if (strings == nullptr) debug_print("String table not found");
/*
* Hacky lookup, if string and symbol tables are found,
* iterate through the strings in string table and check if
* any string matches "HSA_AMD_TOOL_PRIORITY".
* If yes, then add the name of the library to the vector of
* lib names
*/
if (strings != nullptr) {
char* end = strings + limit;
while (strings < end) {
if (strcmp(strings, "HSA_AMD_TOOL_PRIORITY") == 0) {
loadedToolsLib->push_back(info->dlpi_name);
return 0;
}
strings += (strlen(strings) + 1);
}
}
}
}
}
return 0;
}
std::vector<LibHandle> GetLoadedToolsLib() {
std::vector<LibHandle> ret;
std::vector<std::string> names;
/* Iterate through all of the loaded shared libraries in the process */
dl_iterate_phdr(callback, &names);
if (!names.empty()) {
for (auto& name : names) ret.push_back(LoadLib(name));
}
return ret;
}
std::string GetLibraryName(LibHandle lib) {
link_map *map;
if(dlinfo(lib, RTLD_DI_LINKMAP, &map)!=0)
return "";
return map->l_name;
}
Semaphore CreateSemaphore() {
sem_t *sem = new sem_t;
sem_init(sem, 0, 0);
return *(Semaphore*)&sem;
}
bool WaitSemaphore(Semaphore sem) {
while(sem_wait(*(sem_t**)&sem))
if (errno != EINTR) return false;
return true;
}
void PostSemaphore(Semaphore sem) {
if (sem_post(*(sem_t**)&sem))
assert(false && "Failed to post semaphore");
}
void DestroySemaphore(Semaphore sem) {
sem_destroy(*(sem_t**)&sem);
delete *(sem_t**)&sem;
}
Mutex CreateMutex() {
pthread_mutex_t* mutex = new pthread_mutex_t;
pthread_mutex_init(mutex, NULL);
return *(Mutex*)&mutex;
}
bool TryAcquireMutex(Mutex lock) {
return pthread_mutex_trylock(*(pthread_mutex_t**)&lock) == 0;
}
bool AcquireMutex(Mutex lock) {
return pthread_mutex_lock(*(pthread_mutex_t**)&lock) == 0;
}
void ReleaseMutex(Mutex lock) {
pthread_mutex_unlock(*(pthread_mutex_t**)&lock);
}
void DestroyMutex(Mutex lock) {
pthread_mutex_destroy(*(pthread_mutex_t**)&lock);
delete *(pthread_mutex_t**)&lock;
}
void Sleep(int delay_in_millisec) { usleep(delay_in_millisec * 1000); }
void uSleep(int delayInUs) { usleep(delayInUs); }
void YieldThread() { sched_yield(); }
Thread CreateThread(ThreadEntry function, void* threadArgument, uint stackSize) {
os_thread* result = new os_thread(function, threadArgument, stackSize);
if (!result->Valid()) {
delete result;
return nullptr;
}
return reinterpret_cast<Thread>(result);
}
void CloseThread(Thread thread) { delete reinterpret_cast<os_thread*>(thread); }
bool WaitForThread(Thread thread) { return reinterpret_cast<os_thread*>(thread)->Wait(); }
bool WaitForAllThreads(Thread* threads, uint threadCount) {
for (uint i = 0; i < threadCount; i++) WaitForThread(threads[i]);
return true;
}
bool IsEnvVarSet(std::string env_var_name) {
char* buff = NULL;
buff = getenv(env_var_name.c_str());
return (buff != NULL);
}
void SetEnvVar(std::string env_var_name, std::string env_var_value) {
setenv(env_var_name.c_str(), env_var_value.c_str(), 1);
}
int GetProcessId() {
return ::getpid();
}
std::string GetEnvVar(std::string env_var_name) {
char* buff;
buff = getenv(env_var_name.c_str());
std::string ret;
if (buff) {
ret = buff;
}
return ret;
}
size_t GetUserModeVirtualMemorySize() {
#ifdef _LP64
// https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt :
// user space is 0000000000000000 - 00007fffffffffff (=47 bits)
return (size_t)(0x800000000000);
#else
return (size_t)(0xffffffff); // ~4GB
#endif
}
size_t GetUsablePhysicalHostMemorySize() {
struct sysinfo info = {0};
if (sysinfo(&info) != 0) {
return 0;
}
const size_t physical_size =
static_cast<size_t>(info.totalram * info.mem_unit);
return std::min(GetUserModeVirtualMemorySize(), physical_size);
}
uintptr_t GetUserModeVirtualMemoryBase() { return (uintptr_t)0; }
// Os event implementation
typedef struct EventDescriptor_ {
pthread_cond_t event;
pthread_mutex_t mutex;
bool state;
bool auto_reset;
} EventDescriptor;
EventHandle CreateOsEvent(bool auto_reset, bool init_state) {
EventDescriptor* eventDescrp;
eventDescrp = (EventDescriptor*)malloc(sizeof(EventDescriptor));
pthread_mutex_init(&eventDescrp->mutex, NULL);
pthread_cond_init(&eventDescrp->event, NULL);
eventDescrp->auto_reset = auto_reset;
eventDescrp->state = init_state;
EventHandle handle = reinterpret_cast<EventHandle>(eventDescrp);
return handle;
}
int DestroyOsEvent(EventHandle event) {
if (event == NULL) {
return -1;
}
EventDescriptor* eventDescrp = reinterpret_cast<EventDescriptor*>(event);
int ret_code = pthread_cond_destroy(&eventDescrp->event);
ret_code |= pthread_mutex_destroy(&eventDescrp->mutex);
free(eventDescrp);
return ret_code;
}
int WaitForOsEvent(EventHandle event, unsigned int milli_seconds) {
if (event == NULL) {
return -1;
}
EventDescriptor* eventDescrp = reinterpret_cast<EventDescriptor*>(event);
// Event wait time is 0 and state is non-signaled, return directly
if (milli_seconds == 0) {
int tmp_ret = pthread_mutex_trylock(&eventDescrp->mutex);
if (tmp_ret == EBUSY) {
// Timeout
return 1;
}
}
int ret_code = 0;
pthread_mutex_lock(&eventDescrp->mutex);
if (!eventDescrp->state) {
if (milli_seconds == 0) {
ret_code = 1;
} else {
struct timespec ts;
struct timeval tp;
ret_code = gettimeofday(&tp, NULL);
ts.tv_sec = tp.tv_sec;
ts.tv_nsec = tp.tv_usec * 1000;
unsigned int sec = milli_seconds / 1000;
unsigned int mSec = milli_seconds % 1000;
ts.tv_sec += sec;
ts.tv_nsec += mSec * 1000000;
// More then one second, add 1 sec to the tv_sec elem
if (ts.tv_nsec > 1000000000) {
ts.tv_sec += 1;
ts.tv_nsec = ts.tv_nsec - 1000000000;
}
ret_code =
pthread_cond_timedwait(&eventDescrp->event, &eventDescrp->mutex, &ts);
// Time out
if (ret_code == 110) {
ret_code = 0x14003; // 1 means time out in HSA
}
if (ret_code == 0 && eventDescrp->auto_reset) {
eventDescrp->state = false;
}
}
} else if (eventDescrp->auto_reset) {
eventDescrp->state = false;
}
pthread_mutex_unlock(&eventDescrp->mutex);
return ret_code;
}
int SetOsEvent(EventHandle event) {
if (event == NULL) {
return -1;
}
EventDescriptor* eventDescrp = reinterpret_cast<EventDescriptor*>(event);
int ret_code = 0;
ret_code = pthread_mutex_lock(&eventDescrp->mutex);
eventDescrp->state = true;
ret_code = pthread_mutex_unlock(&eventDescrp->mutex);
ret_code |= pthread_cond_signal(&eventDescrp->event);
return ret_code;
}
int ResetOsEvent(EventHandle event) {
if (event == NULL) {
return -1;
}
EventDescriptor* eventDescrp = reinterpret_cast<EventDescriptor*>(event);
int ret_code = 0;
ret_code = pthread_mutex_lock(&eventDescrp->mutex);
eventDescrp->state = false;
ret_code = pthread_mutex_unlock(&eventDescrp->mutex);
return ret_code;
}
static double invPeriod = 0.0;
uint64_t ReadAccurateClock() {
if (invPeriod == 0.0) AccurateClockFrequency();
timespec time;
int err = clock_gettime(CLOCK_MONOTONIC_RAW, &time);
if (err != 0) {
perror("clock_gettime(CLOCK_MONOTONIC_RAW,...) failed");
abort();
}
return (uint64_t(time.tv_sec) * 1000000000ull + uint64_t(time.tv_nsec)) * invPeriod;
}
uint64_t AccurateClockFrequency() {
static clockid_t clock = CLOCK_MONOTONIC;
static std::atomic<bool> first(true);
// Check kernel version - not a concurrency concern.
// use non-RAW for getres due to bug in older 2.6.x kernels
if (first.load(std::memory_order_acquire)) {
utsname kernelInfo;
if (uname(&kernelInfo) == 0) {
try {
std::string ver = kernelInfo.release;
size_t idx;
int major = std::stoi(ver, &idx);
int minor = std::stoi(ver.substr(idx + 1));
if ((major >= 4) && (minor >= 4)) {
clock = CLOCK_MONOTONIC_RAW;
}
} catch (...) {
// Kernel version string doesn't conform to the standard pattern.
// Keep using the "safe" (non-RAW) clock.
}
}
first.store(false, std::memory_order_release);
}
timespec time;
int err = clock_getres(clock, &time);
if (err != 0) {
perror("clock_getres failed");
abort();
}
if (time.tv_sec != 0 || time.tv_nsec >= 0xFFFFFFFF) {
fprintf(stderr,
"clock_getres(CLOCK_MONOTONIC(_RAW),...) returned very low "
"frequency (<1Hz).\n");
abort();
}
if (invPeriod == 0.0) invPeriod = 1.0 / double(time.tv_nsec);
return 1000000000ull / uint64_t(time.tv_nsec);
}
SharedMutex CreateSharedMutex() {
pthread_rwlockattr_t attrib;
int err = pthread_rwlockattr_init(&attrib);
if (err != 0) {
fprintf(stderr, "rw lock attribute init failed: %s\n", strerror(err));
return nullptr;
}
#ifdef __GLIBC__
err = pthread_rwlockattr_setkind_np(&attrib, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
if (err != 0) {
fprintf(stderr, "Set rw lock attribute failure: %s\n", strerror(err));
return nullptr;
}
#else
err = pthread_rwlockattr_setkind(&attrib, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
if (err != 0) {
fprintf(stderr, "Set rw lock attribute failure: %s\n", strerror(err));
return nullptr;
}
#endif
pthread_rwlock_t* lock = new pthread_rwlock_t;
err = pthread_rwlock_init(lock, &attrib);
if (err != 0) {
fprintf(stderr, "rw lock init failed: %s\n", strerror(err));
return nullptr;
}
pthread_rwlockattr_destroy(&attrib);
return lock;
}
bool TryAcquireSharedMutex(SharedMutex lock) {
int err = pthread_rwlock_trywrlock(*(pthread_rwlock_t**)&lock);
return err == 0;
}
bool AcquireSharedMutex(SharedMutex lock) {
int err = pthread_rwlock_wrlock(*(pthread_rwlock_t**)&lock);
return err == 0;
}
void ReleaseSharedMutex(SharedMutex lock) {
int err = pthread_rwlock_unlock(*(pthread_rwlock_t**)&lock);
if (err != 0) {
fprintf(stderr, "SharedMutex unlock failed: %s\n", strerror(err));
abort();
}
}
bool TrySharedAcquireSharedMutex(SharedMutex lock) {
int err = pthread_rwlock_tryrdlock(*(pthread_rwlock_t**)&lock);
return err == 0;
}
bool SharedAcquireSharedMutex(SharedMutex lock) {
int err = pthread_rwlock_rdlock(*(pthread_rwlock_t**)&lock);
return err == 0;
}
void SharedReleaseSharedMutex(SharedMutex lock) {
int err = pthread_rwlock_unlock(*(pthread_rwlock_t**)&lock);
if (err != 0) {
fprintf(stderr, "SharedMutex unlock failed: %s\n", strerror(err));
abort();
}
}
void DestroySharedMutex(SharedMutex lock) {
pthread_rwlock_destroy(*(pthread_rwlock_t**)&lock);
delete *(pthread_rwlock_t**)&lock;
}
static uint64_t sys_clock_period_ = 0;
uint64_t ReadSystemClock() {
struct timespec ts;
clock_gettime(CLOCK_BOOTTIME, &ts);
uint64_t time = (uint64_t(ts.tv_sec) * 1000000000 + uint64_t(ts.tv_nsec));
if (sys_clock_period_ != 1)
return time / sys_clock_period_;
else
return time;
}
uint64_t SystemClockFrequency() {
struct timespec ts;
clock_getres(CLOCK_BOOTTIME, &ts);
sys_clock_period_ = (uint64_t(ts.tv_sec) * 1000000000 + uint64_t(ts.tv_nsec));
return 1000000000 / sys_clock_period_;
}
bool ParseCpuID(cpuid_t* cpuinfo) {
#if defined(__i386__) || defined(__x86_64__)
uint32_t eax, ebx, ecx, edx, max_eax = 0;
memset(cpuinfo, 0, sizeof(*cpuinfo));
/* Make sure current CPU supports at least EAX 4 */
if (!__get_cpuid_max(0x80000004, NULL)) return false;
// Manufacturer ID is a twelve-character ASCII string stored in order EBX, EDX, ECX.
if (!__get_cpuid(0, &max_eax, (uint32_t*)&cpuinfo->ManufacturerID[0],
(uint32_t*)&cpuinfo->ManufacturerID[8],
(uint32_t*)&cpuinfo->ManufacturerID[4])) {
return false;
}
if (!strcmp(cpuinfo->ManufacturerID, "AuthenticAMD")) {
if (__get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx)) {
cpuinfo->mwaitx = !!((ecx >> 29) & 0x1);
}
}
return true;
#else
return false;
#endif
}
} // namespace os
} // namespace rocr
#endif
+290
Parādīt failu
@@ -0,0 +1,290 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
// Library of syncronization primitives - to be added to as needed.
#ifndef HSA_RUNTIME_CORE_UTIL_LOCKS_H_
#define HSA_RUNTIME_CORE_UTIL_LOCKS_H_
#include "utils.h"
#include "os.h"
namespace rocr {
class HybridMutex {
public:
HybridMutex():lock_(0) {
sem_ = os::CreateSemaphore();
}
~HybridMutex() {
os::DestroySemaphore(sem_);
}
bool Try() {
int old = 0;
return lock_.compare_exchange_strong(old, 1);
}
bool Acquire() {
int cnt = maxSpinIterPause + maxSpinIterYield;
int old = 0;
while (!lock_.compare_exchange_strong(old, 1)) {
cnt--;
if (cnt > maxSpinIterPause) {
_mm_pause();
} else if (cnt-- > maxSpinIterYield) {
os::YieldThread();
} else {
os::WaitSemaphore(sem_);
cnt = maxSpinIterPause + maxSpinIterYield;
}
old = 0;
}
return true;
}
void Release() {
int old = 1;
if (lock_.compare_exchange_strong(old, 0))
os::PostSemaphore(sem_);
}
private:
std::atomic<int> lock_;
os::Semaphore sem_;
const uint32_t maxSpinIterPause = 55;
const uint32_t maxSpinIterYield = 55;
/// @brief: Disable copiable and assignable ability.
DISALLOW_COPY_AND_ASSIGN(HybridMutex);
};
/// @brief: a class represents a kernel mutex.
/// Uses the kernel's scheduler to keep the waiting thread from being scheduled
/// until the lock is released (Best for long waits, though anything using
/// a kernel object is a long wait).
class KernelMutex {
public:
KernelMutex() { lock_ = os::CreateMutex(); }
~KernelMutex() { os::DestroyMutex(lock_); }
bool Try() { return os::TryAcquireMutex(lock_); }
bool Acquire() { return os::AcquireMutex(lock_); }
void Release() { os::ReleaseMutex(lock_); }
private:
os::Mutex lock_;
/// @brief: Disable copiable and assignable ability.
DISALLOW_COPY_AND_ASSIGN(KernelMutex);
};
/// @brief: represents a spin lock.
/// For very short hold durations on the order of the thread scheduling
/// quanta or less.
class SpinMutex {
public:
SpinMutex() { lock_ = 0; }
bool Try() {
int old = 0;
return lock_.compare_exchange_strong(old, 1);
}
bool Acquire() {
int old = 0;
while (!lock_.compare_exchange_strong(old, 1))
{
old=0;
os::YieldThread();
}
return true;
}
void Release() { lock_ = 0; }
private:
std::atomic<int> lock_;
/// @brief: Disable copiable and assignable ability.
DISALLOW_COPY_AND_ASSIGN(SpinMutex);
};
class KernelEvent {
public:
KernelEvent() { evt_ = os::CreateOsEvent(true, true); }
~KernelEvent() { os::DestroyOsEvent(evt_); }
bool IsSet() { return os::WaitForOsEvent(evt_, 0)==0; }
bool WaitForSet() { return os::WaitForOsEvent(evt_, 0xFFFFFFFF)==0; }
void Set() { os::SetOsEvent(evt_); }
void Reset() { os::ResetOsEvent(evt_); }
private:
os::EventHandle evt_;
/// @brief: Disable copiable and assignable ability.
DISALLOW_COPY_AND_ASSIGN(KernelEvent);
};
/// @brief: represents a yielding shared mutex.
/// aka read/write mutex
class KernelSharedMutex {
public:
/// @brief: Interfaces ScopedAcquire to shared operations.
class Shared {
public:
explicit Shared(KernelSharedMutex* lock) : lock_(lock) {}
bool Try() { return lock_->TryShared(); }
bool Acquire() { return lock_->AcquireShared(); }
void Release() { lock_->ReleaseShared(); }
private:
KernelSharedMutex* lock_;
};
KernelSharedMutex() { lock_ = os::CreateSharedMutex(); }
~KernelSharedMutex() { os::DestroySharedMutex(lock_); }
// Exclusive mode operations
bool Try() { return os::TryAcquireSharedMutex(lock_); }
bool Acquire() { return os::AcquireSharedMutex(lock_); }
void Release() { os::ReleaseSharedMutex(lock_); }
// Shared mode operations
bool TryShared() { return os::TrySharedAcquireSharedMutex(lock_); }
bool AcquireShared() { return os::SharedAcquireSharedMutex(lock_); }
void ReleaseShared() { os::SharedReleaseSharedMutex(lock_); }
// Return shared operations interface
Shared shared() { return Shared(this); }
private:
os::SharedMutex lock_;
/// @brief: Disable copiable and assignable ability.
DISALLOW_COPY_AND_ASSIGN(KernelSharedMutex);
};
/// @brief: Type trait to identify mutex types
template <class T> class isMutex {
public:
enum { value = false };
};
template <> class isMutex<HybridMutex> {
public:
enum { value = true };
};
template <> class isMutex<KernelMutex> {
public:
enum { value = true };
};
template <> class isMutex<SpinMutex> {
public:
enum { value = true };
};
template <> class isMutex<KernelSharedMutex> {
public:
enum { value = true };
};
/// @brief: A class behaves as a lock in a scope. When trying to enter into the
/// critical section, creat a object of this class. After the control path goes
/// out of the scope, it will release the lock automatically.
template <class LockType> class ScopedAcquire {
public:
/// @brief: When constructing, acquire the lock.
/// @param: lock(Input), pointer to an existing lock.
explicit ScopedAcquire(LockType* lock) : lock_(lock), doRelease(true) {
static_assert(isMutex<LockType>::value, "ScopedAcquire requires a mutex type.");
lock_.Acquire();
}
explicit ScopedAcquire(LockType lock) : lock_(lock), doRelease(true) {
static_assert(!isMutex<LockType>::value, "Mutex types are not copyable.");
lock_.Acquire();
}
/// @brief: when destructing, release the lock.
~ScopedAcquire() {
if (doRelease) lock_.Release();
}
/// @brief: Release the lock early. Avoid using when possible.
void Release() {
lock_.Release();
doRelease = false;
}
private:
/// @brief: Adapts between pointers to mutex types and mutex pointer types.
template <class T, bool B> class container {
public:
container(T* lock) : lock_(lock) {}
__forceinline bool Acquire() { return lock_->Acquire(); }
__forceinline void Release() { return lock_->Release(); }
private:
T* lock_;
};
/// @brief: Specialization for mutex pointer types.
template <class T> class container<T, false> {
public:
container(T lock) : lock_(lock) {}
__forceinline bool Acquire() { return lock_.Acquire(); }
__forceinline void Release() { return lock_.Release(); }
private:
T lock_;
};
container<LockType, isMutex<LockType>::value> lock_;
bool doRelease;
/// @brief: Disable copiable and assignable ability.
DISALLOW_COPY_AND_ASSIGN(ScopedAcquire);
};
} // namespace rocr
#endif // HSA_RUNTIME_CORE_SUTIL_LOCKS_H_
+327
Parādīt failu
@@ -0,0 +1,327 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2024, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
// Minimal operating system abstraction interfaces.
#ifndef HSA_RUNTIME_CORE_UTIL_OS_H_
#define HSA_RUNTIME_CORE_UTIL_OS_H_
#include <string>
#include <vector>
#include "utils.h"
namespace rocr {
namespace os {
typedef void* LibHandle;
typedef void* Semaphore;
typedef void* Mutex;
typedef void* SharedMutex;
typedef void* Thread;
typedef void* EventHandle;
enum class os_t { OS_WIN = 0, OS_LINUX, COUNT };
static __forceinline std::underlying_type<os_t>::type os_index(os_t val) {
return std::underlying_type<os_t>::type(val);
}
#ifdef _WIN32
static const os_t current_os = os_t::OS_WIN;
#elif __linux__
static const os_t current_os = os_t::OS_LINUX;
#else
static_assert(false, "Operating System not detected!");
#endif
/// @brief: Loads dynamic library based on file name. Return value will be NULL
/// if failed.
/// @param: filename(Input), file name of the library.
/// @return: LibHandle.
LibHandle LoadLib(std::string filename);
/// @brief: Gets the address of exported symbol. Return NULl if failed.
/// @param: lib(Input), library handle which exporting from.
/// @param: export_name(Input), the name of the exported symbol.
/// @return: void*.
void* GetExportAddress(LibHandle lib, std::string export_name);
/// @brief: Unloads the dynamic library.
/// @param: lib(Input), library handle which will be unloaded.
void CloseLib(LibHandle lib);
/// @brief: Lists loaded tool libraries that contain
/// symbol HSA_AMD_TOOL_PRIORITY
/// @return: List of library handles
std::vector<LibHandle> GetLoadedToolsLib();
/// @brief: Returns the library's path name.
/// @param: lib(Input), libray handle
/// @return: Path name of library
std::string GetLibraryName(LibHandle lib);
/// @brief: Creates a Semaphore, will return NULL if failed.
/// @param: void.
/// @return: Semaphore.
Semaphore CreateSemaphore();
/// @brief: Waits for the semaphore. This is a blocking wait.
/// If the Semaphore is signalled, this function will return.
/// @param: sem(Input), handle to the semaphore.
/// @return: void.
bool WaitSemaphore(Semaphore sem);
/// @brief: Post/Signal/Wake-up the semaphore
/// @param: sem(Input), handle to the semaphore.
/// @return: void.
void PostSemaphore(Semaphore sem);
/// @brief: Destroys the semaphore.
/// @param: sem(Input), handle to the semaphore.
/// @return: void.
void DestroySemaphore(Semaphore sem);
/// @brief: Creates a mutex, will return NULL if failed.
/// @param: void.
/// @return: Mutex.
Mutex CreateMutex();
/// @brief: Tries to acquire the mutex once, if successed, return true.
/// @param: lock(Input), handle to the mutex.
/// @return: bool.
bool TryAcquireMutex(Mutex lock);
/// @brief: Aquires the mutex, if the mutex is locked, it will wait until it is
/// released. If the mutex is acquired successfully, it will return true.
/// @param: lock(Input), handle to the mutex.
/// @return: bool.
bool AcquireMutex(Mutex lock);
/// @brief: Releases the mutex.
/// @param: lock(Input), handle to the mutex.
/// @return: void.
void ReleaseMutex(Mutex lock);
/// @brief: Destroys the mutex.
/// @param: lock(Input), handle to the mutex.
/// @return: void.
void DestroyMutex(Mutex lock);
/// @brief: Creates a shared mutex, will return NULL if failed.
/// @param: void.
/// @return: SharedMutex.
SharedMutex CreateSharedMutex();
/// @brief: Tries to acquire the mutex in exclusive mode once, if successed, return true.
/// @param: lock(Input), handle to the shared mutex.
/// @return: bool.
bool TryAcquireSharedMutex(SharedMutex lock);
/// @brief: Aquires the mutex in exclusive mode, if the mutex is locked, it will wait until it is
/// released. If the mutex is acquired successfully, it will return true.
/// @param: lock(Input), handle to the mutex.
/// @return: bool.
bool AcquireSharedMutex(SharedMutex lock);
/// @brief: Releases the mutex from exclusive mode.
/// @param: lock(Input), handle to the mutex.
/// @return: void.
void ReleaseSharedMutex(SharedMutex lock);
/// @brief: Tries to acquire the mutex in shared mode once, if successed, return true.
/// @param: lock(Input), handle to the mutex.
/// @return: bool.
bool TrySharedAcquireSharedMutex(SharedMutex lock);
/// @brief: Aquires the mutex in shared mode, if the mutex in exclusive mode, it will wait until it
/// is released. If the mutex is acquired successfully, it will return true.
/// @param: lock(Input), handle to the mutex.
/// @return: bool.
bool SharedAcquireSharedMutex(SharedMutex lock);
/// @brief: Releases the mutex from shared mode.
/// @param: lock(Input), handle to the mutex.
/// @return: void.
void SharedReleaseSharedMutex(SharedMutex lock);
/// @brief: Destroys the mutex.
/// @param: lock(Input), handle to the mutex.
/// @return: void.
void DestroySharedMutex(SharedMutex lock);
/// @brief: Puts current thread to sleep.
/// @param: delayInMs(Input), time in millisecond for sleeping.
/// @return: void.
void Sleep(int delayInMs);
/// @brief: Puts current thread to sleep.
/// @param: delayInMs(Input), time in millisecond for sleeping.
/// @return: void.
void uSleep(int delayInUs);
/// @brief: Yields current thread.
/// @param: void.
/// @return: void.
void YieldThread();
typedef void (*ThreadEntry)(void*);
/// @brief: Creates a thread will return NULL if failed.
/// @param: entry_function(Input), a pointer to the function which the thread
/// starts from.
/// @param: entry_argument(Input), a pointer to the argument of the thread
/// function.
/// @param: stack_size(Input), size of the thread's stack, 0 by default.
/// @return: Thread, a handle to thread created.
Thread CreateThread(ThreadEntry entry_function, void* entry_argument,
uint stack_size = 0);
/// @brief: Destroys the thread.
/// @param: thread(Input), thread handle to what will be destroyed.
/// @return: void.
void CloseThread(Thread thread);
/// @brief: Waits for specific thread to finish, if successful, return true.
/// @param: thread(Input), handle to waiting thread.
/// @return: bool.
bool WaitForThread(Thread thread);
/// @brief: Waits for multiple threads to finish, if successful, return true.
/// @param; threads(Input), a pointer to a list of thread handle.
/// @param: thread_count(Input), number of threads to be waited on.
/// @return: bool.
bool WaitForAllThreads(Thread* threads, uint thread_count);
/// @brief: Determines if environment key is set.
/// @param: env_var_name(Input), name of the environment value.
/// @return: bool, true for binding any value to environment key,
/// including an empty string. False otherwise
bool IsEnvVarSet(std::string env_var_name);
/// @brief: Sets the environment value.
/// @param: env_var_name(Input), name of the environment value.
/// @param: env_var_value(Input), value of the environment value.s
/// @return: void.
void SetEnvVar(std::string env_var_name, std::string env_var_value);
/// @brief: Gets the value of environment value.
/// @param: env_var_name(Input), name of the environment value.
/// @return: std::string, value of the environment value, returned as string.
std::string GetEnvVar(std::string env_var_name);
/// @brief: Gets the process ID.
/// @param: void
/// @return: int, process ID returned as int.
int GetProcessId();
/// @brief: Gets the max virtual memory size accessible to the application.
/// @param: void.
/// @return: size_t, size of the accessible memory to the application.
size_t GetUserModeVirtualMemorySize();
/// @brief: Gets the max physical host system memory size.
/// @param: void.
/// @return: size_t, size of the physical host system memory.
size_t GetUsablePhysicalHostMemorySize();
/// @brief: Gets the virtual memory base address. It is hardcoded to 0.
/// @param: void.
/// @return: uintptr_t, always 0.
uintptr_t GetUserModeVirtualMemoryBase();
/// @brief os event api, create an event
/// @param: auto_reset whether an event can reset the status automatically
/// @param: init_state initial state of the event
/// @return: event handle
EventHandle CreateOsEvent(bool auto_reset, bool init_state);
/// @brief os event api, destroy an event
/// @param: event handle
/// @return: whether destroy is correct
int DestroyOsEvent(EventHandle event);
/// @brief os event api, wait on event
/// @param: event Event handle
/// @param: milli_seconds wait time
/// @return: Indicate success or timeout
int WaitForOsEvent(EventHandle event, unsigned int milli_seconds);
/// @brief os event api, set event state
/// @param: event Event handle
/// @return: Whether event set is correct
int SetOsEvent(EventHandle event);
/// @brief os event api, reset event state
/// @param: event Event handle
/// @return: Whether event reset is correct
int ResetOsEvent(EventHandle event);
/// @brief reads a clock which is deemed to be accurate for elapsed time
/// measurements, though not necessarilly fast to query
/// @return clock counter value
uint64_t ReadAccurateClock();
/// @brief retrieves the frequency in Hz of the unit used in ReadAccurateClock.
/// It does not necessarilly reflect the resolution of the clock, but is the
/// value needed to convert a difference in the clock's counter value to elapsed
/// seconds. This frequency does not change at runtime.
/// @return returns the frequency
uint64_t AccurateClockFrequency();
/// @brief read the system clock which serves as the HSA system clock
/// counter in KFD.
uint64_t ReadSystemClock();
/// @brief read the system clock frequency
uint64_t SystemClockFrequency();
typedef struct cpuid_s {
char ManufacturerID[13]; // 12 char, NULL terminated
bool mwaitx;
} cpuid_t;
/// @brief parse CPUID
/// @param: cpuinfo struct to be filled
bool ParseCpuID(cpuid_t* cpuinfo);
} // namespace os
} // namespace rocr
#endif // HSA_RUNTIME_CORE_UTIL_OS_H_
+363
Parādīt failu
@@ -0,0 +1,363 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
// A simple best fit memory allocator with eager compaction. Manages block sub-allocation.
// For use when memory efficiency is more important than allocation speed.
// O(log n) time.
#ifndef HSA_RUNTME_CORE_UTIL_SIMPLE_HEAP_H_
#define HSA_RUNTME_CORE_UTIL_SIMPLE_HEAP_H_
#include <map>
#include <deque>
#include <utility>
#include "core/util/utils.h"
namespace rocr {
template <typename Allocator> class SimpleHeap {
private:
struct Fragment_T {
typedef std::multimap<size_t, uintptr_t>::iterator ptr_t;
ptr_t free_list_entry_;
struct {
size_t size : 62;
bool discard : 1;
bool free : 1;
};
Fragment_T(ptr_t Iterator, size_t Len, bool Free)
: free_list_entry_(Iterator), size(Len), discard(false), free(Free) {}
Fragment_T() = default;
};
struct Block {
uintptr_t base_ptr_;
size_t length_;
Block(uintptr_t base, size_t length) : base_ptr_(base), length_(length) {}
Block() = default;
};
Allocator block_allocator_;
std::multimap<size_t, uintptr_t> free_list_;
std::map<uintptr_t, std::map<uintptr_t, Fragment_T>> block_list_;
std::deque<Block> block_cache_;
// Size of blocks that are at least partially in use.
size_t in_use_size_;
// Total size of block cache
size_t cache_size_;
__forceinline bool isFree(const Fragment_T& node) { return node.free; }
__forceinline void setUsed(Fragment_T& node) {
node.free = false;
node.free_list_entry_ = free_list_.end();
}
__forceinline void setFree(Fragment_T& node, typename Fragment_T::ptr_t Iterator) {
node.free_list_entry_ = Iterator;
node.free = true;
}
__forceinline Fragment_T makeFragment(size_t Len) {
return Fragment_T(free_list_.end(), Len, false);
}
__forceinline Fragment_T makeFragment(typename Fragment_T::ptr_t Iterator, size_t Len) {
return Fragment_T(Iterator, Len, true);
}
__forceinline void removeFreeListEntry(Fragment_T& node) {
if (node.free_list_entry_ != free_list_.end()) {
free_list_.erase(node.free_list_entry_);
node.free_list_entry_ = free_list_.end();
}
}
__forceinline void discard(Fragment_T& node) {
removeFreeListEntry(node);
node.discard = true;
}
public:
explicit SimpleHeap(const Allocator& BlockAllocator = Allocator())
: block_allocator_(BlockAllocator), in_use_size_(0), cache_size_(0) {}
~SimpleHeap() {
trim();
// Leak here may be due to the user. Check is for debugging only.
// assert(in_use_size_ == 0 && "Leak in SimpleHeap.");
}
SimpleHeap(const SimpleHeap& rhs) = delete;
SimpleHeap(SimpleHeap&& rhs) = delete;
SimpleHeap& operator=(const SimpleHeap& rhs) = delete;
SimpleHeap& operator=(SimpleHeap&& rhs) = delete;
void* alloc(size_t bytes) {
// Find best fit.
uintptr_t base;
size_t size;
// For bytes >= 2MB, the requested mem should be aligned
size_t align_bytes = bytes;
const int retry = bytes >= GPU_HUGE_PAGE_SIZE ? 1 : 0;
size_t align = bytes >= GPU_HUGE_PAGE_SIZE ? GPU_HUGE_PAGE_SIZE : DEFAULT_GPU_PAGE_SIZE;
for (int i = 0; i <= retry; i++) {
auto free_fragment = free_list_.lower_bound(align_bytes);
if (free_fragment == free_list_.end()) break;
uintptr_t addr = free_fragment->second;
size = free_fragment->first;
assert(size >= bytes && "SimpleHeap: map lower_bound failure.");
// Find the containing block and fragment
auto it = block_list_.upper_bound(addr);
it--;
auto& frag_map = it->second;
const auto& fragment = frag_map.find(addr);
assert(fragment != frag_map.end() && "Inconsistency in SimpleHeap.");
assert(size == fragment->second.size && "Inconsistency in SimpleHeap.");
size_t delta = addr & (align - 1);
if (!delta) {
// already find aligned address
base = addr;
free_list_.erase(free_fragment);
// Sub-allocate from fragment.
fragment->second.size = bytes;
setUsed(fragment->second);
// Record remaining free space.
if (size > bytes) {
free_fragment = free_list_.insert(std::make_pair(size - bytes, base + bytes));
frag_map[base + bytes] = makeFragment(free_fragment, size - bytes);
}
} else {
// If this is the first request and the requested size is not enough for alignment,
// then request for a bigger hole and do trim.
if (i == 0 && size < bytes + align - delta) {
align_bytes += align;
continue;
}
uintptr_t aligned_base = addr + align - delta;
base = aligned_base;
// Erase the old free list
free_list_.erase(free_fragment);
// fragment 1 - free
free_fragment = free_list_.insert(std::make_pair(aligned_base - addr, addr));
frag_map[addr] = makeFragment(free_fragment, aligned_base - addr);
//fragment 2 - used
frag_map[base] = makeFragment(bytes);
// fragement 3 - free
if (size > aligned_base - addr + bytes) {
free_fragment = free_list_.insert(std::make_pair(size - (aligned_base - addr) - bytes, aligned_base + bytes));
frag_map[aligned_base + bytes] = makeFragment(free_fragment, size - (aligned_base - addr) - bytes);
}
}
return reinterpret_cast<void*>(base);
}
// No usable fragment, check block cache
if (bytes < default_block_size() && !block_cache_.empty()) {
const auto& block = block_cache_.back();
base = block.base_ptr_;
size = block.length_;
block_cache_.pop_back();
cache_size_ -= size;
} else { // Alloc new block - new block may be larger than default.
void* ptr = block_allocator_.alloc(bytes, size);
base = reinterpret_cast<uintptr_t>(ptr);
assert(ptr != nullptr && "Block allocation failed, Allocator is expected to throw.");
}
in_use_size_ += size;
assert(size >= bytes && "Alloc exceeds block size.");
// Sub alloc and insert free region.
if (size > bytes) {
auto free_fragment = free_list_.insert(std::make_pair(size - bytes, base + bytes));
block_list_[base][base + bytes] = makeFragment(free_fragment, size - bytes);
}
// Track used region
block_list_[base][base] = makeFragment(bytes);
// Disallow multiple suballocation from large blocks.
// Prevents a small allocation from retaining a large block.
if (bytes > default_block_size()) {
bool err = discardBlock(reinterpret_cast<void*>(base));
assert(err && "Large block discard failed.");
}
return reinterpret_cast<void*>(base);
}
bool free(void* ptr) {
if (ptr == nullptr) return true;
uintptr_t base = reinterpret_cast<uintptr_t>(ptr);
// Find fragment and validate.
auto frag_map_it = block_list_.upper_bound(base);
if (frag_map_it == block_list_.begin()) return false;
frag_map_it--;
auto& frag_map = frag_map_it->second;
auto fragment = frag_map.find(base);
if (fragment == frag_map.end() || isFree(fragment->second)) return false;
bool discard = fragment->second.discard;
// Merge lower
if (fragment != frag_map.begin()) {
auto lower = fragment;
lower--;
if (isFree(lower->second)) {
removeFreeListEntry(lower->second);
lower->second.size += fragment->second.size;
frag_map.erase(fragment);
fragment = lower;
}
}
// Merge upper
{
auto upper = fragment;
upper++;
if ((upper != frag_map.end()) && isFree(upper->second)) {
removeFreeListEntry(upper->second);
fragment->second.size += upper->second.size;
frag_map.erase(upper);
}
}
// Release whole free blocks.
if (frag_map.size() == 1) {
Block block(fragment->first, fragment->second.size);
block_list_.erase(frag_map_it);
// Discard or add to the block cache.
if (discard) {
block_allocator_.free(reinterpret_cast<void*>(block.base_ptr_), block.length_);
} else {
block_cache_.push_back(block);
cache_size_ += block.length_;
in_use_size_ -= block.length_;
}
balance();
// Don't publish free space since block was moved to the cache.
return true;
}
// Don't report free memory if discarding the fragment.
if (discard) return true;
// Report free fragment
const auto& freeEntry =
free_list_.insert(std::make_pair(size_t(fragment->second.size), fragment->first));
setFree(fragment->second, freeEntry);
return true;
}
void balance() {
// Release old blocks when over cache limit.
while ((block_cache_.size() > 1) && (cache_size_ > in_use_size_ * 2)) {
const auto& block = block_cache_.front();
block_allocator_.free(reinterpret_cast<void*>(block.base_ptr_), block.length_);
cache_size_ -= block.length_;
block_cache_.pop_front();
}
}
void trim() {
for (const auto& block : block_cache_)
block_allocator_.free(reinterpret_cast<void*>(block.base_ptr_), block.length_);
block_cache_.clear();
cache_size_ = 0;
}
size_t cache_size() const { return cache_size_; }
size_t default_block_size() const { return block_allocator_.block_size(); }
// Prevent reuse of the block containing ptr. No further fragments will be allocated from the
// block and the block will not be added to the block cache when it is free.
bool discardBlock(void* ptr) {
if (ptr == nullptr) return true;
uintptr_t base = reinterpret_cast<uintptr_t>(ptr);
// Find block validate.
auto frag_map_it = block_list_.upper_bound(base);
if (frag_map_it == block_list_.begin()) return false;
frag_map_it--;
auto& frag_map = frag_map_it->second;
if ((base < frag_map.begin()->first) ||
(frag_map.rbegin()->first + frag_map.rbegin()->second.size <= base))
return false;
// Is block already discarded?
if (frag_map.begin()->second.discard) return true;
// Mark all fragments for discard and compute block size. Removes freelist records for all
// fragments in the block.
size_t size = 0;
for (auto& frag : frag_map) {
discard(frag.second);
size += frag.second.size;
}
// Remove discarded block from in-use tracking and rebalance the block cache.
in_use_size_ -= size;
balance();
return true;
}
};
} // namespace rocr
#endif // HSA_RUNTME_CORE_UTIL_SIMPLE_HEAP_H_
+185
Parādīt failu
@@ -0,0 +1,185 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#include "small_heap.h"
namespace rocr {
// Inserts node into freelist after place.
// Assumes node will not be an end of the list (list has guard nodes).
void SmallHeap::insertafter(SmallHeap::iterator_t place, SmallHeap::iterator_t node) {
assert(place->first < node->first && "Order violation");
assert(isfree(place->second) && "Freelist operation error.");
iterator_t next = place->second.next;
node->second.next = next;
node->second.prior = place;
place->second.next = node;
next->second.prior = node;
}
// Removes node from freelist.
// Assumes node will not be an end of the list (list has guard nodes).
void SmallHeap::remove(SmallHeap::iterator_t node) {
assert(isfree(node->second) && "Freelist operation error.");
node->second.prior->second.next = node->second.next;
node->second.next->second.prior = node->second.prior;
setused(node->second);
}
// Returns high if merge failed or the merged node.
SmallHeap::memory_t::iterator SmallHeap::merge(SmallHeap::memory_t::iterator low,
SmallHeap::memory_t::iterator high) {
assert(isfree(low->second) && "Merge with allocated block");
assert(isfree(high->second) && "Merge with allocated block");
if ((char*)low->first + low->second.len != (char*)high->first) return high;
assert(!islastfree(high->second) && "Illegal merge.");
low->second.len += high->second.len;
low->second.next = high->second.next;
high->second.next->second.prior = low;
memory.erase(high);
return low;
}
void SmallHeap::free(void* ptr) {
if (ptr == nullptr) return;
auto iterator = memory.find(ptr);
// Check for illegal free
if (iterator == memory.end()) {
assert(false && "Illegal free.");
return;
}
// Return memory to total and link node into free list
total_free += iterator->second.len;
// Could also traverse the free list which might be faster in some cases.
auto before = iterator;
before--;
while (!isfree(before->second)) before--;
assert(before->second.next->first > iterator->first && "Inconsistency in small heap.");
insertafter(before, iterator);
// Attempt compaction
iterator = merge(before, iterator);
merge(iterator, iterator->second.next);
// Update lowHighBondary
high.erase(ptr);
}
void* SmallHeap::alloc(size_t bytes) {
// Is enough memory available?
if ((bytes > total_free) || (bytes == 0)) return nullptr;
iterator_t current;
// Walk the free list and allocate at first fitting location
current = firstfree();
while (!islastfree(current->second)) {
if (bytes <= current->second.len) {
// Decrement from total
total_free -= bytes;
// Split node
if (bytes != current->second.len) {
void* remaining = (char*)current->first + bytes;
Node& node = memory[remaining];
node.len = current->second.len - bytes;
current->second.len = bytes;
insertafter(current, memory.find(remaining));
}
remove(current);
return current->first;
}
current = current->second.next;
}
assert(current->second.len == 0 && "Freelist corruption.");
// Can't service the request due to fragmentation
return nullptr;
}
void* SmallHeap::alloc_high(size_t bytes) {
// Is enough memory available?
if ((bytes > total_free) || (bytes == 0)) return nullptr;
iterator_t current;
// Walk the free list and allocate at first fitting location
current = lastfree();
while (!isfirstfree(current->second)) {
if (bytes <= current->second.len) {
// Decrement from total
total_free -= bytes;
void* alloc;
// Split node
if (bytes != current->second.len) {
alloc = (char*)current->first + current->second.len - bytes;
current->second.len -= bytes;
Node& node = memory[alloc];
node.len = bytes;
setused(node);
} else {
alloc = current->first;
remove(current);
}
high.insert(alloc);
return alloc;
}
current = current->second.prior;
}
assert(current->second.len == 0 && "Freelist corruption.");
// Can't service the request due to fragmentation
return nullptr;
}
} // namespace rocr
+131
Parādīt failu
@@ -0,0 +1,131 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
// A simple first fit memory allocator with eager compaction. For use with few
// items (where list iteration is faster than trees).
// Not thread safe!
#ifndef HSA_RUNTME_CORE_UTIL_SMALL_HEAP_H_
#define HSA_RUNTME_CORE_UTIL_SMALL_HEAP_H_
#include <map>
#include <set>
#include "utils.h"
namespace rocr {
class SmallHeap {
private:
struct Node;
typedef std::map<void*, Node> memory_t;
typedef memory_t::iterator iterator_t;
struct Node {
size_t len;
iterator_t next;
iterator_t prior;
};
SmallHeap(const SmallHeap& rhs) = delete;
SmallHeap& operator=(const SmallHeap& rhs) = delete;
void* const pool;
const size_t length;
size_t total_free;
memory_t memory;
std::set<void*> high;
__forceinline bool isfree(const Node& node) const { return node.next != memory.begin(); }
__forceinline bool islastfree(const Node& node) const { return node.next == memory.end(); }
__forceinline bool isfirstfree(const Node& node) const { return node.prior == memory.end(); }
__forceinline void setlastfree(Node& node) { node.next = memory.end(); }
__forceinline void setfirstfree(Node& node) { node.prior = memory.end(); }
__forceinline void setused(Node& node) { node.next = memory.begin(); }
__forceinline iterator_t firstfree() { return memory.begin()->second.next; }
__forceinline iterator_t lastfree() { return memory.rbegin()->second.prior; }
void insertafter(iterator_t place, iterator_t node);
void remove(iterator_t node);
iterator_t merge(iterator_t low, iterator_t high);
public:
SmallHeap() : pool(nullptr), length(0), total_free(0) {}
SmallHeap(void* base, size_t length)
: pool(base), length(length), total_free(length) {
assert(pool != nullptr && "Invalid base address.");
assert(pool != (void*)0xFFFFFFFFFFFFFFFFull && "Invalid base address.");
assert((char*)pool + length != (char*)0xFFFFFFFFFFFFFFFFull && "Invalid pool bounds.");
Node& start = memory[0];
Node& node = memory[pool];
Node& end = memory[(void*)0xFFFFFFFFFFFFFFFFull];
start.len = 0;
start.next = memory.find(pool);
setfirstfree(start);
node.len = length;
node.prior = memory.begin();
node.next = --memory.end();
end.len = 0;
end.prior = start.next;
setlastfree(end);
high.insert((void*)0xFFFFFFFFFFFFFFFFull);
}
void* alloc(size_t bytes);
void* alloc_high(size_t bytes);
void free(void* ptr);
void* base() const { return pool; }
size_t size() const { return length; }
size_t remaining() const { return total_free; }
void* high_split() const { return *high.begin(); }
};
} // namespace rocr
#endif
+111
Parādīt failu
@@ -0,0 +1,111 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#include "core/util/timer.h"
namespace rocr {
namespace timer {
accurate_clock::init::init() {
freq = os::AccurateClockFrequency();
accurate_clock::period_ns = 1e9 / double(freq);
}
// Calibrates the fast clock using the accurate clock.
fast_clock::init::init() {
typedef accurate_clock clock;
clock::duration delay(std::chrono::milliseconds(1));
// calibrate clock
fast_clock::raw_rep min = 0;
clock::duration elapsed;
do {
elapsed = clock::duration::max();
for (int t = 0; t < 10; t++) {
fast_clock::raw_rep r1, r2;
clock::time_point t0, t1, t2, t3;
t0 = clock::now();
std::atomic_signal_fence(std::memory_order_acq_rel);
r1 = fast_clock::raw_now();
std::atomic_signal_fence(std::memory_order_acq_rel);
t1 = clock::now();
std::atomic_signal_fence(std::memory_order_acq_rel);
do {
t2 = clock::now();
} while (t2 - t1 < delay);
std::atomic_signal_fence(std::memory_order_acq_rel);
r2 = fast_clock::raw_now();
std::atomic_signal_fence(std::memory_order_acq_rel);
t3 = clock::now();
// If elapsed time is shorter than last recorded time and both the start
// and end times are confirmed correlated then record the clock readings.
// This protects against inaccuracy due to thread switching
if ((t3 - t1 < elapsed) && ((t1 - t0) * 10 < (t2 - t1)) &&
((t3 - t2) * 10 < (t2 - t1))) {
elapsed = t3 - t1;
min = r2 - r1;
}
}
delay += delay;
} while (min < 1000);
fast_clock::freq = double(min) / duration_in_seconds(elapsed);
fast_clock::period_ps = 1e12 / fast_clock::freq;
// printf("Timer setup took %f ms\n", duration_in_seconds(elapsed)*1000.0f);
// printf("Fast clock frequency: %f MHz\n", double(fast_clock::freq)/1e6);
}
double accurate_clock::period_ns;
accurate_clock::raw_frequency accurate_clock::freq;
accurate_clock::init accurate_clock::accurate_clock_init;
double fast_clock::period_ps;
fast_clock::raw_frequency fast_clock::freq;
fast_clock::init fast_clock::fast_clock_init;
} // namespace timer
} // namespace rocr
+173
Parādīt failu
@@ -0,0 +1,173 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef HSA_RUNTIME_CORE_UTIL_TIMER_H_
#define HSA_RUNTIME_CORE_UTIL_TIMER_H_
#include "core/util/utils.h"
#include "core/util/os.h"
#include <chrono>
#include <time.h>
#include <type_traits>
namespace rocr {
namespace timer {
// Needed to patch around a mixed arithmetic bug in MSVC's duration_cast as of
// VS 2013.
template <bool isFloat, bool isSigned>
struct wide_type {
typedef double type;
};
template <>
struct wide_type<false, false> {
typedef uintmax_t type;
};
template <>
struct wide_type<false, true> {
typedef intmax_t type;
};
template <typename To, typename Rep, typename Period>
static __forceinline To
duration_cast(const std::chrono::duration<Rep, Period>& d) {
typedef typename wide_type<std::is_floating_point<Rep>::value,
std::is_signed<Rep>::value>::type wide;
typedef std::chrono::duration<wide, typename To::period> unit_convert_t;
unit_convert_t temp = std::chrono::duration_cast<unit_convert_t>(d);
return To(static_cast<typename To::rep>(temp.count()));
}
// End patch
template <typename Rep, typename Period>
static __forceinline double duration_in_seconds(
std::chrono::duration<Rep, Period> delta) {
typedef std::chrono::duration<double, std::ratio<1, 1>> seconds;
return seconds(delta).count();
}
template <typename rep>
static __forceinline rep duration_from_seconds(double delta) {
typedef std::chrono::duration<double, std::ratio<1, 1>> seconds;
return std::chrono::duration_cast<rep>(seconds(delta));
}
// Provices a C++11 standard clock interface to the os::AccurateClock functions
class accurate_clock {
public:
typedef double rep;
typedef std::nano period;
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<accurate_clock> time_point;
static const bool is_steady = true;
static __forceinline time_point now() {
return time_point(duration(raw_now() * period_ns));
}
// These two extra APIs and types let us use clocks without conversion to the
// arbitrary period unit
typedef uint64_t raw_rep;
typedef uint64_t raw_frequency;
static __forceinline raw_rep raw_now() { return os::ReadAccurateClock(); }
static __forceinline raw_frequency raw_freq() { return freq; }
private:
static double period_ns;
static raw_frequency freq;
class init {
public:
init();
};
static init accurate_clock_init;
};
// Provices a C++11 standard clock interface to the lowest latency approximate
// clock
class fast_clock {
public:
typedef double rep;
typedef std::pico period;
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<fast_clock> time_point;
static const bool is_steady = true;
static __forceinline time_point now() {
return time_point(duration(raw_now() * period_ps));
}
// These two extra APIs and types let us use clocks without conversion to the
// arbitrary period unit
typedef uint64_t raw_rep;
typedef double raw_frequency;
#if defined(__x86_64__) || defined(_M_X64)
static __forceinline raw_rep raw_now() { return __rdtsc(); }
static __forceinline raw_frequency raw_freq() { return freq; }
#else
static __forceinline raw_rep raw_now() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
return (raw_rep(ts.tv_sec) * 1000000000 + raw_rep(ts.tv_nsec));
}
static __forceinline raw_frequency raw_freq() { return 1.e-9; }
#endif
private:
static double period_ps;
static raw_frequency freq;
class init {
public:
init();
};
static init fast_clock_init;
};
} // namespace timer
} // namespace rocr
#endif
+424
Parādīt failu
@@ -0,0 +1,424 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2024, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
// Generally useful utility functions
#ifndef HSA_RUNTIME_CORE_UTIL_UTILS_H_
#define HSA_RUNTIME_CORE_UTIL_UTILS_H_
#include "stdint.h"
#include "stddef.h"
#include "stdlib.h"
#include "stdarg.h"
#include "unistd.h"
#include <assert.h>
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
#include <thread>
namespace rocr {
extern FILE* log_file;
extern uint8_t log_flags[8];
typedef unsigned int uint;
typedef uint64_t uint64;
#if defined(__GNUC__)
#if defined(__i386__) || defined(__x86_64__)
#include <x86intrin.h>
#endif
// 2MB huge page size
#define GPU_HUGE_PAGE_SIZE (2 << 20)
// 4KB page size
#define DEFAULT_GPU_PAGE_SIZE (1 << 12)
#define __forceinline __inline__ __attribute__((always_inline))
#define __declspec(x) __attribute__((x))
#undef __stdcall
#define __stdcall // __attribute__((__stdcall__))
#define __ALIGNED__(x) __attribute__((aligned(x)))
void log_printf(const char* file, int line, const char* format, ...);
static __forceinline void* _aligned_malloc(size_t size, size_t alignment) {
#ifdef _ISOC11_SOURCE
return aligned_alloc(alignment, size);
#else
void *mem = NULL;
if (0 != posix_memalign(&mem, alignment, size)) return NULL;
return mem;
#endif
}
static __forceinline void _aligned_free(void* ptr) { return free(ptr); }
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
#include "intrin.h"
#define __ALIGNED__(x) __declspec(align(x))
#if (_MSC_VER < 1800) // < VS 2013
static __forceinline unsigned long long int strtoull(const char* str,
char** endptr, int base) {
return static_cast<unsigned long long>(_strtoui64(str, endptr, base));
}
#endif
#if (_MSC_VER < 1900) // < VS 2015
#define thread_local __declspec(thread)
#endif
#else
#error "Compiler and/or processor not identified."
#endif
#define STRING2(x) #x
#define STRING(x) STRING2(x)
#define PASTE2(x, y) x##y
#define PASTE(x, y) PASTE2(x, y)
#ifdef NDEBUG
#define debug_warning_n(exp, limit) \
do { \
} while (false)
#else
#define debug_warning_n(exp, limit) \
do { \
static std::atomic<int> count(0); \
if (!(exp) && (limit == 0 || count < limit)) { \
fprintf(stderr, "Warning: " STRING(exp) " in %s, " __FILE__ ":" STRING(__LINE__) "\n", \
__PRETTY_FUNCTION__); \
count++; \
} \
} while (false)
#endif
#define debug_warning(exp) debug_warning_n((exp), 0)
#ifdef NDEBUG
#define debug_print(fmt, ...) \
do { \
} while (false)
#else
#define debug_print(fmt, ...) \
do { \
fprintf(stderr, fmt, ##__VA_ARGS__); \
} while (false)
#endif
#ifdef NDEBUG
#define ifdebug if (false)
#else
#define ifdebug if (true)
#endif
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define LogPrint(flag, format, ...) \
do { \
if (hsa_flag_isset64(log_flags, flag)) \
rocr::log_printf(__FILENAME__, __LINE__, format, ##__VA_ARGS__); \
} while (false);
// A macro to disallow the copy and move constructor and operator= functions
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&) = delete; \
TypeName(TypeName&&) = delete; \
void operator=(const TypeName&) = delete; \
void operator=(TypeName&&) = delete;
template <typename lambda>
class ScopeGuard {
public:
explicit __forceinline ScopeGuard(const lambda& release)
: release_(release), dismiss_(false) {}
ScopeGuard(ScopeGuard& rhs) { *this = rhs; }
__forceinline ~ScopeGuard() {
if (!dismiss_) release_();
}
__forceinline ScopeGuard& operator=(ScopeGuard& rhs) {
dismiss_ = rhs.dismiss_;
release_ = rhs.release_;
rhs.dismiss_ = true;
return *this;
}
__forceinline void Dismiss() { dismiss_ = true; }
private:
lambda release_;
bool dismiss_;
};
template <typename lambda>
static __forceinline ScopeGuard<lambda> MakeScopeGuard(lambda rel) {
return ScopeGuard<lambda>(rel);
}
#define MAKE_SCOPE_GUARD_HELPER(lname, sname, ...) \
auto lname = __VA_ARGS__; \
ScopeGuard<decltype(lname)> sname(lname);
#define MAKE_SCOPE_GUARD(...) \
MAKE_SCOPE_GUARD_HELPER(PASTE(scopeGuardLambda, __COUNTER__), \
PASTE(scopeGuard, __COUNTER__), __VA_ARGS__)
#define MAKE_NAMED_SCOPE_GUARD(name, ...) \
MAKE_SCOPE_GUARD_HELPER(PASTE(scopeGuardLambda, __COUNTER__), name, \
__VA_ARGS__)
/// @brief: Finds out the min one of two inputs, input must support ">"
/// operator.
/// @param: a(Input), a reference to type T.
/// @param: b(Input), a reference to type T.
/// @return: T.
template <class T>
static __forceinline T Min(const T& a, const T& b) {
return (a > b) ? b : a;
}
template <class T, class... Arg>
static __forceinline T Min(const T& a, const T& b, Arg... args) {
return Min(a, Min(b, args...));
}
/// @brief: Find out the max one of two inputs, input must support ">" operator.
/// @param: a(Input), a reference to type T.
/// @param: b(Input), a reference to type T.
/// @return: T.
template <class T>
static __forceinline T Max(const T& a, const T& b) {
return (b > a) ? b : a;
}
template <class T, class... Arg>
static __forceinline T Max(const T& a, const T& b, Arg... args) {
return Max(a, Max(b, args...));
}
/// @brief: Free the memory space which is newed previously.
/// @param: ptr(Input), a pointer to memory space. Can't be NULL.
/// @return: void.
struct DeleteObject {
template <typename T>
void operator()(const T* ptr) const {
delete ptr;
}
};
/// @brief: Checks if a value is power of two, if it is, return true. Be careful
/// when passing 0.
/// @param: val(Input), the data to be checked.
/// @return: bool.
template <typename T>
static __forceinline bool IsPowerOfTwo(T val) {
return (val & (val - 1)) == 0;
}
/// @brief: Calculates the floor value aligned based on parameter of alignment.
/// If value is at the boundary of alignment, it is unchanged.
/// @param: value(Input), value to be calculated.
/// @param: alignment(Input), alignment value.
/// @return: T.
template <typename T>
static __forceinline T AlignDown(T value, size_t alignment) {
return (T)((value / alignment) * alignment);
}
/// @brief: Same as previous one, but first parameter becomes pointer, for more
/// info, see the previous desciption.
/// @param: value(Input), pointer to type T.
/// @param: alignment(Input), alignment value.
/// @return: T*, pointer to type T.
template <typename T>
static __forceinline T* AlignDown(T* value, size_t alignment) {
return (T*)AlignDown((intptr_t)value, alignment);
}
/// @brief: Calculates the ceiling value aligned based on parameter of
/// alignment.
/// If value is at the boundary of alignment, it is unchanged.
/// @param: value(Input), value to be calculated.
/// @param: alignment(Input), alignment value.
/// @param: T.
template <typename T>
static __forceinline T AlignUp(T value, size_t alignment) {
return AlignDown((T)(value + alignment - 1), alignment);
}
/// @brief: Same as previous one, but first parameter becomes pointer, for more
/// info, see the previous desciption.
/// @param: value(Input), pointer to type T.
/// @param: alignment(Input), alignment value.
/// @return: T*, pointer to type T.
template <typename T>
static __forceinline T* AlignUp(T* value, size_t alignment) {
return (T*)AlignDown((intptr_t)((uint8_t*)value + alignment - 1), alignment);
}
/// @brief: Checks if the input value is at the boundary of alignment, if it is,
/// @return true.
/// @param: value(Input), value to be checked.
/// @param: alignment(Input), alignment value.
/// @return: bool.
template <typename T>
static __forceinline bool IsMultipleOf(T value, size_t alignment) {
return (AlignUp(value, alignment) == value);
}
/// @brief: Same as previous one, but first parameter becomes pointer, for more
/// info, see the previous desciption.
/// @param: value(Input), pointer to type T.
/// @param: alignment(Input), alignment value.
/// @return: bool.
template <typename T>
static __forceinline bool IsMultipleOf(T* value, size_t alignment) {
return (AlignUp(value, alignment) == value);
}
static __forceinline uint32_t NextPow2(uint32_t value) {
if (value == 0) return 1;
uint32_t v = value - 1;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return v + 1;
}
static __forceinline uint64_t NextPow2(uint64_t value) {
if (value == 0) return 1;
uint64_t v = value - 1;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
return v + 1;
}
static __forceinline bool strIsEmpty(const char* str) noexcept { return str[0] == '\0'; }
static __forceinline std::string& ltrim(std::string& s) {
auto it = std::find_if(s.begin(), s.end(),
[](char c) { return !std::isspace<char>(c, std::locale::classic()); });
s.erase(s.begin(), it);
return s;
}
static __forceinline std::string& rtrim(std::string& s) {
auto it = std::find_if(s.rbegin(), s.rend(),
[](char c) { return !std::isspace<char>(c, std::locale::classic()); });
s.erase(it.base(), s.end());
return s;
}
static __forceinline std::string& trim(std::string& s) { return ltrim(rtrim(s)); }
} // namespace rocr
template <uint32_t lowBit, uint32_t highBit, typename T>
static __forceinline uint32_t BitSelect(T p) {
static_assert(sizeof(T) <= sizeof(uintptr_t), "Type out of range.");
static_assert(highBit < sizeof(uintptr_t) * 8, "Bit index out of range.");
uintptr_t ptr = p;
if (highBit != (sizeof(uintptr_t) * 8 - 1))
return (uint32_t)((ptr & ((1ull << (highBit + 1)) - 1)) >> lowBit);
else
return (uint32_t)(ptr >> lowBit);
}
inline uint32_t PtrLow16Shift8(const void* p) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
return (uint32_t)((ptr & 0xFFFFULL) >> 8);
}
inline uint32_t PtrHigh64Shift16(const void* p) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
return (uint32_t)((ptr & 0xFFFFFFFFFFFF0000ULL) >> 16);
}
inline uint32_t PtrLow40Shift8(const void* p) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
return (uint32_t)((ptr & 0xFFFFFFFFFFULL) >> 8);
}
inline uint32_t PtrHigh64Shift40(const void* p) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
return (uint32_t)((ptr & 0xFFFFFF0000000000ULL) >> 40);
}
static inline uint8_t Ptr48High8(const void* p) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
return (uint8_t)((ptr & 0xFF0000000000ULL) >> 40);
}
static inline uint32_t Ptr48Low32(const void* p) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
assert((ptr & 0xFFFFFFFFFF00ULL) == ptr);
return (uint32_t)((ptr & 0xFFFFFFFFFFULL) >> 8);
}
inline uint32_t PtrLow32(const void* p) {
return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(p));
}
inline uint32_t PtrHigh32(const void* p) {
uint32_t ptr = 0;
#ifdef HSA_LARGE_MODEL
ptr = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(p) >> 32);
#endif
return ptr;
}
inline uint32_t HighPart(uint64_t value) {
return (value & 0xFFFFFFFF00000000) >> 32;
}
inline uint32_t LowPart(uint64_t value) {
return (value & 0x00000000FFFFFFFF);
}
#include "atomic_helpers.h"
#endif // HSA_RUNTIME_CORE_UTIL_UTILS_H_
+327
Parādīt failu
@@ -0,0 +1,327 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32 // Are we compiling for windows?
#define NOMINMAX
#include "core/util/os.h"
#include <algorithm>
#include <process.h>
#include <string>
#include <windows.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include <xmmintrin.h>
#undef Yield
#undef CreateMutex
namespace rocr {
namespace os {
static_assert(sizeof(LibHandle) == sizeof(HMODULE),
"OS abstraction size mismatch");
static_assert(sizeof(LibHandle) == sizeof(::HANDLE),
"OS abstraction size mismatch");
static_assert(sizeof(Semaphore) == sizeof(::HANDLE),
"OS abstraction size mismatch");
static_assert(sizeof(Mutex) == sizeof(::HANDLE),
"OS abstraction size mismatch");
static_assert(sizeof(Thread) == sizeof(::HANDLE),
"OS abstraction size mismatch");
static_assert(sizeof(EventHandle) == sizeof(::HANDLE),
"OS abstraction size mismatch");
LibHandle LoadLib(std::string filename) {
HMODULE ret = LoadLibrary(filename.c_str());
return *(LibHandle*)&ret;
}
void* GetExportAddress(LibHandle lib, std::string export_name) {
return GetProcAddress(*(HMODULE*)&lib, export_name.c_str());
}
void CloseLib(LibHandle lib) { FreeLibrary(*(::HMODULE*)&lib); }
std::vector<LibHandle> GetLoadedLibs() {
// Use EnumProcessModulesEx
static_assert(false, "Not implemented.");
}
std::string GetLibraryName(LibHandle lib) {
static_assert(false, "Not implemented.");
}
Semaphore CreateSemaphore() {
sem = static_cast<void*>(CreateSemaphore(NULL, 0, LONG_MAX, NULL));
assert(sem != NULL && "CreateSemaphore failed");
return *(Semaphore*)&sem;
}
bool WaitSemaphore(Semaphore sem) {
return WaitForSingleObject(*(::HANDLE*)&lock, INFINITE) == WAIT_OBJECT_0;
}
void PostSemaphore(Semaphore sem) {
ReleaseSemaphore(static_cast<HANDLE>(*sem), 1, NULL);
}
void DestroySemaphore(Semaphore sem) {
if (!CloseHandle(static_cast<HANDLE>(*sem))) {
assert("CloseHandle() failed");
}
*sem = NULL;
}
Mutex CreateMutex() { return CreateEvent(NULL, false, true, NULL); }
bool TryAcquireMutex(Mutex lock) {
return WaitForSingleObject(*(::HANDLE*)&lock, 0) == WAIT_OBJECT_0;
}
bool AcquireMutex(Mutex lock) {
return WaitForSingleObject(*(::HANDLE*)&lock, INFINITE) == WAIT_OBJECT_0;
}
void ReleaseMutex(Mutex lock) { SetEvent(*(::HANDLE*)&lock); }
void DestroyMutex(Mutex lock) { CloseHandle(*(::HANDLE*)&lock); }
void Sleep(int delay_in_millisecond) { ::Sleep(delay_in_millisecond); }
void uSleep(int delayInUs) { ::Sleep(delayInUs / 1000); }
void YieldThread() { ::Sleep(0); }
struct ThreadArgs {
void* entry_args;
ThreadEntry entry_function;
};
unsigned __stdcall ThreadTrampoline(void* arg) {
ThreadArgs* thread_args = (ThreadArgs*)arg;
ThreadEntry entry = thread_args->entry_function;
void* data = thread_args->entry_args;
delete thread_args;
entry(data);
_endthreadex(0);
return 0;
}
Thread CreateThread(ThreadEntry entry_function, void* entry_argument,
uint stack_size) {
ThreadArgs* thread_args = new ThreadArgs();
thread_args->entry_args = entry_argument;
thread_args->entry_function = entry_function;
uintptr_t ret =
_beginthreadex(NULL, stack_size, ThreadTrampoline, thread_args, 0, NULL);
return *(Thread*)&ret;
}
void CloseThread(Thread thread) { CloseHandle(*(::HANDLE*)&thread); }
bool WaitForThread(Thread thread) {
return WaitForSingleObject(*(::HANDLE*)&thread, INFINITE) == WAIT_OBJECT_0;
}
bool WaitForAllThreads(Thread* threads, uint thread_count) {
return WaitForMultipleObjects(thread_count, threads, TRUE, INFINITE) ==
WAIT_OBJECT_0;
}
void SetEnvVar(std::string env_var_name, std::string env_var_value) {
SetEnvironmentVariable(env_var_name.c_str(), env_var_value.c_str());
}
std::string GetEnvVar(std::string env_var_name) {
char* buff;
DWORD char_count = GetEnvironmentVariable(env_var_name.c_str(), NULL, 0);
if (char_count == 0) return "";
buff = (char*)alloca(sizeof(char) * char_count);
GetEnvironmentVariable(env_var_name.c_str(), buff, char_count);
buff[char_count - 1] = '\0';
std::string ret = buff;
return ret;
}
size_t GetUserModeVirtualMemorySize() {
SYSTEM_INFO system_info = {0};
GetSystemInfo(&system_info);
return ((size_t)system_info.lpMaximumApplicationAddress + 1);
}
size_t GetUsablePhysicalHostMemorySize() {
MEMORYSTATUSEX memory_status = {0};
memory_status.dwLength = sizeof(memory_status);
if (GlobalMemoryStatusEx(&memory_status) == 0) {
return 0;
}
const size_t physical_size = static_cast<size_t>(memory_status.ullTotalPhys);
return std::min(GetUserModeVirtualMemorySize(), physical_size);
}
uintptr_t GetUserModeVirtualMemoryBase() { return (uintptr_t)0; }
// Os event wrappers
EventHandle CreateOsEvent(bool auto_reset, bool init_state) {
EventHandle evt = reinterpret_cast<EventHandle>(
CreateEvent(NULL, (BOOL)(!auto_reset), (BOOL)init_state, NULL));
return evt;
}
int DestroyOsEvent(EventHandle event) {
if (event == NULL) {
return -1;
}
return CloseHandle(reinterpret_cast<::HANDLE>(event));
}
int WaitForOsEvent(EventHandle event, unsigned int milli_seconds) {
if (event == NULL) {
return -1;
}
int ret_code =
WaitForSingleObject(reinterpret_cast<::HANDLE>(event), milli_seconds);
if (ret_code == WAIT_TIMEOUT) {
ret_code = 0x14003; // 0x14003 indicates timeout
}
return ret_code;
}
int SetOsEvent(EventHandle event) {
if (event == NULL) {
return -1;
}
return SetEvent(reinterpret_cast<::HANDLE>(event));
}
int ResetOsEvent(EventHandle event) {
if (event == NULL) {
return -1;
}
return ResetEvent(reinterpret_cast<::HANDLE>(event));
}
uint64_t ReadAccurateClock() {
uint64_t ret;
QueryPerformanceCounter((LARGE_INTEGER*)&ret);
return ret;
}
uint64_t AccurateClockFrequency() {
uint64_t ret;
QueryPerformanceFrequency((LARGE_INTEGER*)&ret);
return ret;
}
SharedMutex CreateSharedMutex() {
assert(false && "Not implemented.");
abort();
return nullptr;
}
bool TryAcquireSharedMutex(SharedMutex lock) {
assert(false && "Not implemented.");
abort();
return false;
}
bool AcquireSharedMutex(SharedMutex lock) {
assert(false && "Not implemented.");
abort();
return false;
}
void ReleaseSharedMutex(SharedMutex lock) {
assert(false && "Not implemented.");
abort();
}
bool TrySharedAcquireSharedMutex(SharedMutex lock) {
assert(false && "Not implemented.");
abort();
return false;
}
bool SharedAcquireSharedMutex(SharedMutex lock) {
assert(false && "Not implemented.");
abort();
return false;
}
void SharedReleaseSharedMutex(SharedMutex lock) {
assert(false && "Not implemented.");
abort();
}
void DestroySharedMutex(SharedMutex lock) {
assert(false && "Not implemented.");
abort();
}
uint64_t ReadSystemClock() {
assert(false && "Not implemented.");
abort();
return 0;
}
uint64_t SystemClockFrequency() {
assert(false && "Not implemented.");
abort();
return 0;
}
bool ParseCpuID(cpuid_t* cpuinfo) {
assert(false && "Not implemented.");
abort();
return false;
}
} // namespace os
} // namespace rocr
#endif
+52
Parādīt failu
@@ -0,0 +1,52 @@
/*
* Copyright © 2014 Advanced Micro Devices, Inc.
*
* 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 (including
* the next paragraph) 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.
*/
#include "libhsakmt.h"
HSAKMT_STATUS HSAKMTAPI hsaKmtGetVersion(HsaVersionInfo *VersionInfo) {
CHECK_DXG_OPEN();
VersionInfo->KernelInterfaceMajorVersion = 1;
VersionInfo->KernelInterfaceMinorVersion = 16;
return HSAKMT_STATUS_SUCCESS;
}
HSAKMT_STATUS HSAKMTAPI
hsaKmtGetVersionCapInfo(HsaVersionCapability *VersionCapInfo) {
CHECK_DXG_OPEN();
VersionCapInfo->Value = 0;
/*VersionCapInfo->ui64.InterruptSignal = 0;
VersionCapInfo->ui64.Sdma = 0;
VersionCapInfo->ui64.SdmaXgmi = 0;
VersionCapInfo->ui64.Image = 0;
VersionCapInfo->ui64.EventAge = 0;
VersionCapInfo->ui64.Scratch = 0;
VersionCapInfo->ui64.Vmem = 0;
VersionCapInfo->ui64.dmabuf = 0;
VersionCapInfo->ui64.XNack = 0;*/
return HSAKMT_STATUS_SUCCESS;
}
+281
Parādīt failu
@@ -0,0 +1,281 @@
/* Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. */
#include "inc/wddm/cmd_util.h"
namespace rocr {
namespace core {
/*
* Builds a COPY_DATA packet that copies data.
*/
size_t CmdUtil::BuildCopyData(
uint64_t *pDstAddr,
void *pBuffer,
uint32_t dstSel,
uint32_t dstCachePolicy,
uint32_t srcSel,
uint32_t srcCachePolicy,
uint32_t countSel,
uint32_t wrConfirm) {
PM4MEC_COPY_DATA copy_data = {0};
GenerateCmdHeader(&copy_data, IT_COPY_DATA);
copy_data.bitfields2.dst_sel = dstSel;
copy_data.bitfields2.src_sel = srcSel;
copy_data.bitfields2.dst_cache_policy = dstCachePolicy;
copy_data.bitfields2.src_cache_policy = srcCachePolicy;
copy_data.bitfields2.count_sel = countSel;
copy_data.bitfields2.wr_confirm = wrConfirm;
copy_data.bitfields5c.dst_64b_addr_lo = (PtrLow32(pDstAddr) >> 3);
copy_data.dst_addr_hi = PtrHigh32(pDstAddr);
memcpy(pBuffer, &copy_data, sizeof(copy_data));
return sizeof(copy_data);
}
/*
* Builds a EVENT_WRITE packet.
* Applications can use Barrier command to ensure their
* command is executed only after all other commands have
* completed their execution.
*/
size_t CmdUtil::BuildBarrier(
void *pBuffer,
uint32_t eventIndex,
uint32_t eventType) {
BarrierTemplate barrier = {0};
GenerateCmdHeader(&barrier.event_write, IT_EVENT_WRITE);
barrier.event_write.bitfields2.event_index = eventIndex;
barrier.event_write.bitfields2.event_type = eventType;
memcpy(pBuffer, &barrier, sizeof(barrier));
return sizeof(barrier);
}
/*
* Builds a ACQUIRE_MEM packet.
* Users can submit this command to
* invalidate Gpu caches - L1 and or L2.
*/
size_t CmdUtil::BuildAcquireMem(
uint8_t major,
void *pBuffer) {
size_t ret;
if (major == 9) {
gfx9::AcquireMemTemplate acq = {0};
GenerateCmdHeader(&acq.acquire_mem, IT_ACQUIRE_MEM);
// Specify the size of memory to invalidate. Size is
// specified in terms of 256 byte chunks. A coher_size
// of 0xFFFFFFFF actually specified 0xFFFFFFFF00 (40 bits)
// of memory. The field coher_size_hi specifies memory from
// bits 40-64 for a total of 256 TB.
acq.acquire_mem.coher_size = 0xFFFFFFFF;
acq.acquire_mem.bitfields4.coher_size_hi = 0xFF;
// Specify the address of memory to invalidate. The
// address must be 256 byte aligned.
acq.acquire_mem.coher_base_lo = 0;
acq.acquire_mem.bitfields6.coher_base_hi = 0;
// Specify the poll interval for determing if operation is complete
acq.acquire_mem.bitfields7.poll_interval = 4;
acq.acquire_mem.bitfields2.coher_cntl =
(1 << 29) | // CP_COHER_CNTL__SH_ICACHE_ACTION_ENA_MASK
(1 << 27) | // CP_COHER_CNTL__SH_KCACHE_ACTION_ENA_MASK
(1 << 28); // CP_COHER_CNTL__SH_KCACHE_VOL_ACTION_ENA_MASK
memcpy(pBuffer, &acq, sizeof(acq));
ret = sizeof(acq);
} else if (major >= 10) {
gfx10::AcquireMemTemplate acq = {0};
GenerateCmdHeader(&acq.acquire_mem, IT_ACQUIRE_MEM);
acq.acquire_mem.coher_size = 0xFFFFFFFF;
acq.acquire_mem.bitfields4.coher_size_hi = 0xFF;
acq.acquire_mem.coher_base_lo = 0;
acq.acquire_mem.bitfields6.coher_base_hi = 0;
acq.acquire_mem.bitfields7.poll_interval = 4;
acq.acquire_mem.bitfields8.gcr_cntl =
(1 << 16) | // SEQ = FORWARD
(1 << 15) | // GL2_WB
(1 << 14) | // GL2_INV
(1 << 9) | // GL1_INV
(1 << 8) | // GLV_INV
(1 << 7) | // GLK_INV
(1 << 6) | // GLK_WB
(1 << 5) | // GLM_INV
(1 << 4) | // GLM_WB
(1 << 0); // GLI_INV = ALL
memcpy(pBuffer, &acq, sizeof(acq));
ret = sizeof(acq);
}
return ret;
}
/*
* Builds a scratch packet.
*/
size_t CmdUtil::BuildScratch(
void *pScratchBase,
void *pBuffer) {
struct SetScratchTemplate scratch = {0};
GenerateSetShRegHeader(&scratch, mmCOMPUTE_DISPATCH_SCRATCH_BASE_LO);
scratch.scratch_lo = Ptr48Low32(pScratchBase);
scratch.scratch_hi = Ptr48High8(pScratchBase);
memcpy(pBuffer, &scratch, sizeof(scratch));
return sizeof(scratch);
}
/**
* @ Set Compute Shader parameter for gfx11 and above
*/
size_t CmdUtil::BuildComputeShaderParams(void *pBuffer) {
struct DispatchProgramResourceRegs compute_shader_params = {0};
GenerateSetShRegHeader(&compute_shader_params, mmCOMPUTE_PGM_RSRC3);
// IMAGE_OP: Indicates the compute program contains an image op
// instruction and should be stalled by its WAIT_SYNC fence.
compute_shader_params.compute_pgm_rsrc3 = (1 << 31);
memcpy(pBuffer, &compute_shader_params, sizeof(compute_shader_params));
return sizeof(compute_shader_params);
}
/*
* Builds a dispatch packet.
*/
size_t CmdUtil::BuildDispatch(
struct DispatchInfo *pInfo,
void *pBuffer) {
DispatchTemplate dispatch = {0};
GenerateSetShRegHeader(&dispatch.dimension_regs, mmCOMPUTE_NUM_THREAD_X);
dispatch.dimension_regs.compute_num_thread_x = pInfo->pPacket->workgroup_size_x;
dispatch.dimension_regs.compute_num_thread_y = pInfo->pPacket->workgroup_size_y;
dispatch.dimension_regs.compute_num_thread_z = pInfo->pPacket->workgroup_size_z;
// TODO: Add AQL packet index for debugger
// Debugger requires AQL packet index in COMPUTE_DISPATCH_PKT_ADDR_LO
GenerateSetShRegHeader(&dispatch.program_regs, mmCOMPUTE_PGM_LO);
dispatch.program_regs.compute_pgm_lo = Ptr48Low32(pInfo->pEntry);
dispatch.program_regs.compute_pgm_hi = Ptr48High8(pInfo->pEntry);
GenerateSetShRegHeader(&dispatch.program_resource_regs, mmCOMPUTE_PGM_RSRC1);
dispatch.program_resource_regs.compute_pgm_rsrc1 = pInfo->pKernelObject->compute_pgm_rsrc1;
dispatch.program_resource_regs.compute_pgm_rsrc2 =
(pInfo->ldsBlks << 15) | pInfo->pKernelObject->compute_pgm_rsrc2;
GenerateSetShRegHeader(&dispatch.resource_regs, mmCOMPUTE_RESOURCE_LIMITS);
dispatch.resource_regs.compute_resource_limits = 0x3ff;
dispatch.resource_regs.compute_static_thread_mgmt_se0 = 0xFFFFFFFF;
dispatch.resource_regs.compute_static_thread_mgmt_se1 = 0xFFFFFFFF;
dispatch.resource_regs.compute_static_thread_mgmt_se2 = 0xFFFFFFFF;
dispatch.resource_regs.compute_static_thread_mgmt_se3 = 0xFFFFFFFF;
dispatch.resource_regs.compute_tmpring_size = pInfo->pAmdQueue->compute_tmpring_size;
GenerateSetShRegHeader(&dispatch.compute_user_data_regs, mmCOMPUTE_USER_DATA_0);
uint32_t sgpr_no = 0;
if (AMD_HSA_BITS_GET(pInfo->pKernelObject->kernel_code_properties,
AMD_KERNEL_CODE_PROPERTIES_ENABLE_SGPR_PRIVATE_SEGMENT_BUFFER)) {
assert(pInfo->major < 11);
pInfo->scratchBaseOffset[pInfo->offsetCnt++] =
offsetof(struct DispatchTemplate, compute_user_data_regs.compute_user_data[0]) +
sgpr_no * sizeof(uint32_t);
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
pInfo->pAmdQueue->scratch_resource_descriptor[0];
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
pInfo->pAmdQueue->scratch_resource_descriptor[1];
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
pInfo->pAmdQueue->scratch_resource_descriptor[2];
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
pInfo->srd;
}
if (AMD_HSA_BITS_GET(pInfo->pKernelObject->kernel_code_properties,
AMD_KERNEL_CODE_PROPERTIES_ENABLE_SGPR_DISPATCH_PTR)) {
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] = PtrLow32(pInfo->pPacket);
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] = PtrHigh32(pInfo->pPacket);
}
if (AMD_HSA_BITS_GET(pInfo->pKernelObject->kernel_code_properties,
AMD_KERNEL_CODE_PROPERTIES_ENABLE_SGPR_QUEUE_PTR)) {
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] = PtrLow32(pInfo->pAmdQueue);
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] = PtrHigh32(pInfo->pAmdQueue);
}
if (AMD_HSA_BITS_GET(pInfo->pKernelObject->kernel_code_properties,
AMD_KERNEL_CODE_PROPERTIES_ENABLE_SGPR_KERNARG_SEGMENT_PTR)) {
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
PtrLow32(pInfo->pPacket->kernarg_address);
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
PtrHigh32(pInfo->pPacket->kernarg_address);
}
if (AMD_HSA_BITS_GET(pInfo->pKernelObject->kernel_code_properties,
AMD_KERNEL_CODE_PROPERTIES_ENABLE_SGPR_DISPATCH_ID)) {
// This feature may be enabled as a side effect of indirect calls.
// However, the compiler team confirmed that the dispatch id itself is not used,
// so safe to send 0 for each dispatch.
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] = NULL;
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] = NULL;
}
if (AMD_HSA_BITS_GET(pInfo->pKernelObject->kernel_code_properties,
AMD_KERNEL_CODE_PROPERTIES_ENABLE_SGPR_FLAT_SCRATCH_INIT)) {
assert(pInfo->major < 11);
pInfo->scratchBaseOffset[pInfo->offsetCnt++] =
offsetof(struct DispatchTemplate, compute_user_data_regs.compute_user_data[0]) +
sgpr_no * sizeof(uint32_t);
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
PtrLow32(pInfo->pScratchBase);
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
PtrHigh32(pInfo->pScratchBase);
}
if (AMD_HSA_BITS_GET(pInfo->pKernelObject->kernel_code_properties,
AMD_KERNEL_CODE_PROPERTIES_ENABLE_SGPR_PRIVATE_SEGMENT_SIZE)) {
dispatch.compute_user_data_regs.compute_user_data[sgpr_no++] =
pInfo->scratchSizePerWave / (pInfo->wave32 ? 32 : 64);
}
GenerateCmdHeader(&dispatch.dispatch_direct, IT_DISPATCH_DIRECT);
dispatch.dispatch_direct.dispatch_initiator =
(1 << 0) | // COMPUTE_SHADER_EN
(1 << 2) | // FORCE_START_AT_000
(1 << 5); // USE_THREAD_DIMENSIONS
if (pInfo->wave32) dispatch.dispatch_direct.dispatch_initiator |= (1 << 15); // CS_W32_EN
dispatch.dispatch_direct.dim_x = pInfo->pPacket->grid_size_x;
dispatch.dispatch_direct.dim_y = pInfo->pPacket->grid_size_y;
dispatch.dispatch_direct.dim_z = pInfo->pPacket->grid_size_z;
memcpy(pBuffer, &dispatch, sizeof(dispatch));
return sizeof(dispatch);
}
/*
* Builds a ATOMIC_MEM packet.
* Users can submit this command
* to perform atomic operations.
*/
size_t CmdUtil::BuildAtomicMem(
uint64_t *pAddr,
uint32_t atomic,
void *pBuffer,
uint32_t cachePolicy,
uint64_t srcData) {
AtomicTemplate atom = {0};
GenerateCmdHeader(&atom.atomic, IT_ATOMIC_MEM);
atom.atomic.addr_lo = PtrLow32(pAddr);
atom.atomic.addr_hi = PtrHigh32(pAddr);
atom.atomic.bitfields2.atomic = atomic;
atom.atomic.bitfields2.cache_policy = cachePolicy;
atom.atomic.src_data_lo = LowPart(srcData);
atom.atomic.src_data_hi = HighPart(srcData);
memcpy(pBuffer, &atom, sizeof(atom));
return sizeof(atom);
}
} // namespace core
} // namespace rocr
+879
Parādīt failu
@@ -0,0 +1,879 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#include <cinttypes>
#include <bitset>
#include <sys/mman.h>
#include <sys/sysinfo.h>
#include <linux/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include "inc/wddm/status.h"
#include "inc/wddm/types.h"
#include "inc/wddm/device.h"
#include "inc/wddm/queue.h"
namespace rocr {
namespace core {
const uint32_t WDDMDevice::cmdbuf_aql_frame_num_ = 0x1000;
WDDMDevice::WDDMDevice(D3DKMT_HANDLE adapter, LUID adapter_luid)
: adapter_(adapter), adapter_luid_(adapter_luid) {
memset(&device_info_, 0, sizeof(device_info_));
ParseDeviceInfo();
CreateDevice();
SetPowerOptimization(false);
CreatePagingQueue();
ReserveLocalHeapSpace();
ReserveSystemHeapSpace();
InitVaMgr();
InitCmdbufInfo();
}
WDDMDevice::~WDDMDevice() {
FreeLocalHeapSpace();
FreeSystemHeapSpace();
DestroyPagingQueue();
SetPowerOptimization(true);
DestroyDevice();
DestroyDeviceInfo();
}
static NTSTATUS WDDMQueryAdapter(D3DKMT_HANDLE adapter, KMTQUERYADAPTERINFOTYPE type,
void *data, int size)
{
D3DKMT_QUERYADAPTERINFO args = {0};
args.hAdapter = adapter;
args.Type = type;
args.pPrivateDriverData = data;
args.PrivateDriverDataSize = size;
return D3DKMTQueryAdapterInfo(&args);
}
uint64_t WDDMDevice::VramAvail(void) {
D3DKMT_QUERYSTATISTICS stats;
NTSTATUS ret;
uint64_t usedVis = 0;
uint64_t usedInv = 0;
// wait fence complete
uint64_t value = page_fence_value_.load();
if(!CpuWait(&page_syncobj_, &value, 1, false))
return HSA_STATUS_ERROR;
// local cpu-visible memory
memset(&stats, 0, sizeof(D3DKMT_QUERYSTATISTICS));
stats.Type = D3DKMT_QUERYSTATISTICS_SEGMENT;
stats.AdapterLuid = adapter_luid_;
stats.QuerySegment.SegmentId = 0;
ret = D3DKMTQueryStatistics(&stats);
if (ret == 0)
usedVis = stats.QueryResult.SegmentInformation.BytesResident;
// local invisible memory
memset(&stats, 0, sizeof(D3DKMT_QUERYSTATISTICS));
stats.Type = D3DKMT_QUERYSTATISTICS_SEGMENT;
stats.AdapterLuid = adapter_luid_;
stats.QuerySegment.SegmentId = 1;
ret = D3DKMTQueryStatistics(&stats);
if (ret == 0)
usedInv = stats.QueryResult.SegmentInformation.BytesResident;
return LocalHeapSize() - usedVis - usedInv;
}
bool WDDMDevice::CreateDevice(void) {
D3DKMT_CREATEDEVICE args = {0};
args.hAdapter = adapter_;
NTSTATUS ret = D3DKMTCreateDevice(&args);
if (ret == STATUS_SUCCESS) {
device_ = args.hDevice;
return true;
}
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::DestroyDevice(void) {
D3DKMT_DESTROYDEVICE args = {0};
args.hDevice = device_;
NTSTATUS ret = D3DKMTDestroyDevice(&args);
if (ret == STATUS_SUCCESS)
return true;
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::CreatePagingQueue(void) {
D3DKMT_CREATEPAGINGQUEUE args = {0};
args.hDevice = device_;
args.Priority = D3DDDI_PAGINGQUEUE_PRIORITY_NORMAL;
NTSTATUS ret = D3DKMTCreatePagingQueue(&args);
if (ret == STATUS_SUCCESS) {
page_queue_ = args.hPagingQueue;
page_syncobj_ = args.hSyncObject;
page_fence_addr_ = (uint64_t *)args.FenceValueCPUVirtualAddress;
page_fence_value_ = 0;
return true;
}
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::DestroyPagingQueue(void) {
D3DDDI_DESTROYPAGINGQUEUE args = {0};
args.hPagingQueue = page_queue_;
NTSTATUS ret = D3DKMTDestroyPagingQueue(&args);
if (ret == STATUS_SUCCESS)
return true;
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::CommitSystemHeapSpace(void* addr, int64_t size, bool lock) {
int32_t protFlags = PROT_READ | PROT_WRITE | PROT_EXEC;
int32_t mapFlags = MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|
MAP_NORESERVE|MAP_UNINITIALIZED;
if (lock)
mapFlags |= MAP_LOCKED;
void* paddr = mmap(addr, size, protFlags, mapFlags, -1, 0);
if (paddr == MAP_FAILED) {
fprintf(stderr, "%s fail to commit %s addr = %p, paddr = %p\n",
__FUNCTION__, (lock ? "locked" : ""), addr, paddr);
return false;
}
assert(addr == paddr);
/*if (!Runtime::runtime_singleton_->PinWARequired())
return true;*/
/*
* Do not make the pages in this range available to the child
* after a fork(2). This is useful to prevent copy-on-write
* semantics from changing the physical location of a page if
* the parent writes to it after a fork(2). (Such page
* relocations cause problems for hardware that DMAs into the
* page.)
*
* https://man7.org/linux/man-pages/man2/madvise.2.html
*/
if (madvise(addr, size, MADV_DONTFORK))
fprintf(stderr, "%s fail to set MADV_DONTFORK for addr = %p\n",
__FUNCTION__, addr);
return true;
}
bool WDDMDevice::DecommitSystemHeapSpace(void* addr, int64_t size) {
int32_t protFlags = PROT_NONE;
int32_t mapFlags = MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|
MAP_NORESERVE|MAP_UNINITIALIZED;
void* paddr = mmap(addr, size, protFlags, mapFlags, -1, 0);
if (paddr == MAP_FAILED) {
fprintf(stderr, "%s fail to decommit addr = %p, paddr = %p\n",
__FUNCTION__, addr, paddr);
return false;
}
assert(addr == paddr);
return true;
}
bool WDDMDevice::ReserveSystemHeapSpace() {
struct sysinfo info;
int ret = sysinfo(&info);
uint64_t max_ram = 0x10000000000;
uint64_t alignment = 0x100000000;
assert(!ret);
int32_t protFlags = PROT_NONE;
// minimum of reserve size is 8G, maximum of reserve size is 1T.
system_heap_space_size_ = std::min(AlignUp(info.totalram, alignment) * 2, max_ram);
void* cpu = mmap(NULL, system_heap_space_size_, protFlags,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (cpu == MAP_FAILED) {
fprintf(stderr, "%s fail to reserve system_heap_space_size_ = %lx \n",
__FUNCTION__, system_heap_space_size_);
return false;
}
system_heap_space_start_ = (uint64_t)cpu;
return true;
}
bool WDDMDevice::FreeSystemHeapSpace(void) {
void *cpu = (void *)system_heap_space_start_;
if (munmap(cpu, system_heap_space_size_ != 0)) {
fprintf(stderr, "%s fail to unmap = %p \n", __FUNCTION__, cpu);
return false;
}
return true;
}
/*
* To find the avaliable same range for cpu
* virtual space and gpu virtual space.
* sys_va_size of cpu va range is larger 1G
* than gpu va range, otherwise ReserveGPUVirtualAddress
* will return error.
*/
bool WDDMDevice::ReserveLocalHeapSpace(void) {
uint64_t sys_va[16] = {0};
uint64_t local_va;
uint64_t sys_va_size;
int match_index = -1;
uint64_t align = 0x40000000; /* 1G */
void* ptr = NULL;
local_heap_space_start_ = 0;
local_heap_space_size_ = AlignUp(LocalHeapSize(), align) * 4;
sys_va_size = local_heap_space_size_ + align;
/* it will retry 16 times to find the avaliable range. */
for (int i = 0; i < 16; i++) {
local_va = 0;
ptr = mmap(NULL, sys_va_size , PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED) {
fprintf(stderr, "%s fail to reserve cpu va in %d time!\n",
__FUNCTION__, i);
break;
}
sys_va[i] = (uint64_t)ptr;
if (thunk::ReserveGpuVirtualAddress(
adapter_, local_heap_space_size_,
(uint64_t)ptr,
(uint64_t)ptr + sys_va_size, &local_va) == ErrorCode::Success) {
match_index = i;
local_heap_space_start_ = local_va;
debug_print("%s success to reserve gpu va %lx and va cpu %p in %d time\n",
__FUNCTION__, local_va, ptr, i);
break;
} else {
debug_print("%s fail to reserve gpu va for cpu va %p in %d time!\n",
__FUNCTION__, ptr, i);
}
}
if (match_index >= 0) {
/* release cpu unused ranges*/
uint64_t left_size = local_va - sys_va[match_index];
uint64_t right_size = align - left_size;
if ((left_size > 0) && munmap((void*)sys_va[match_index], left_size))
fprintf(stderr, "%s fail to unmap left %lx with size %lx\n",
__FUNCTION__, sys_va[match_index], left_size);
if ((right_size > 0) && munmap((void*)(local_va + local_heap_space_size_), right_size))
fprintf(stderr, "%s fail to unmap right %lx with size %lx\n",
__FUNCTION__, (local_va + local_heap_space_size_), right_size);
} else {
fprintf(stderr, "%s fail to reserve Local Heap Space !\n",
__FUNCTION__);
}
/* free match fail address for cpu va */
int free = match_index >= 0 ? match_index : 16;
for (int j = 0; j < free; j++) {
if (sys_va[j] != 0 && munmap((void*)sys_va[j], sys_va_size)) {
fprintf(stderr, "%s fail to unmap %d %lx\n", __FUNCTION__, j, sys_va[j]);
}
}
return match_index >= 0;
}
bool WDDMDevice::FreeLocalHeapSpace(void) {
thunk::FreeGpuVirtualAddress(adapter_, local_heap_space_start_, local_heap_space_size_);
void *cpu = (void *)local_heap_space_start_;
return munmap(cpu, local_heap_space_size_) == 0;
}
void WDDMDevice::InitVaMgr() {
uint32_t min_align = 4096;
local_va_mgr_ = std::make_unique<VaMgr>(local_heap_space_start_, local_heap_space_size_, min_align);
}
void WDDMDevice::SetPowerOptimization(bool restore) {
void *priv_data;
int priv_size;
priv_size = rocr_proxy::CreatePowerOptPrivData(&priv_data, restore);
D3DKMT_ESCAPE d3dkmt_escape;
memset(&d3dkmt_escape, 0, sizeof(d3dkmt_escape));
d3dkmt_escape.hAdapter = adapter_;
d3dkmt_escape.hDevice = device_;
d3dkmt_escape.hContext = 0; //KMD only use device to identify the process
d3dkmt_escape.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
d3dkmt_escape.pPrivateDriverData = priv_data;
d3dkmt_escape.PrivateDriverDataSize = priv_size;
d3dkmt_escape.Flags.HardwareAccess = true;
NTSTATUS status = D3DKMTEscape(&d3dkmt_escape);
debug_print("%s status %d restore %d\n", __FUNCTION__, status, restore);
rocr_proxy::DestroyPrivData(priv_data);
}
ErrorCode WDDMDevice::ReserveGpuVirtualAddress(const rocr_proxy::AllocDomain domain,
gpusize hit_base_addr, gpusize size,
gpusize *out_gpu_virt_addr, gpusize alignment, bool lock) {
gpusize gpu_addr = 0;
ErrorCode code = ErrorCode::Success;
if (domain == rocr_proxy::kSystem) {
code = thunk::ReserveGpuVirtualAddress(adapter_, size,
system_heap_space_start_,
system_heap_space_start_ + system_heap_space_size_,
&gpu_addr);
if (code != ErrorCode::Success)
return code;
if (!CommitSystemHeapSpace((void*)gpu_addr, size, lock)) {
thunk::FreeGpuVirtualAddress(adapter_, gpu_addr, size);
code = ErrorCode::SyscallFail;
}
} else {
uint64_t align = alignment == 0 ? (64 * 1024) : alignment; // default 64K alignment
if (domain == rocr_proxy::kLocal && size >= GPU_HUGE_PAGE_SIZE)
align = GPU_HUGE_PAGE_SIZE;
gpu_addr = local_va_mgr_->Alloc(size, align, hit_base_addr);
if (gpu_addr == 0)
code = ErrorCode::OutOfGpuMemory;
}
*out_gpu_virt_addr = (code == ErrorCode::Success) ? gpu_addr : 0;
return code;
}
ErrorCode WDDMDevice::FreeGpuVirtualAddress(const rocr_proxy::AllocDomain domain,
gpusize gpu_addr, gpusize size) {
auto code = ErrorCode::Success;
if (domain == rocr_proxy::kSystem) {
DecommitSystemHeapSpace((void *)gpu_addr, size);
thunk::FreeGpuVirtualAddressArgs free_args{};
free_args.hAdapter = adapter_;
free_args.BaseAddress = gpu_addr;
free_args.Size = size;
code = thunk::FreeGpuVirtualAddress(&free_args);
} else {
local_va_mgr_->Free(gpu_addr);
}
return code;
}
void WDDMDevice::UpdatePageFence(uint64_t fence_value) {
uint64_t current = page_fence_value_.load();
// atomically set fence value when target is bigger than current one
do {
if (current >= fence_value)
break;
} while (!page_fence_value_.compare_exchange_weak(current, fence_value));
}
ErrorCode WDDMDevice::CreateGpuMemory(const GpuMemoryCreateInfo &create_info, GpuMemory **gpu_mem) {
ErrorCode ret;
*gpu_mem = nullptr;
auto mem = new GpuMemory(this);
if (create_info.dmabuf_fd > 0)
ret = mem->ImportPhysicalHandle(create_info.dmabuf_fd);
else
ret = mem->Init(create_info);
if (ret == ErrorCode::Success)
*gpu_mem = mem;
return ret;
}
void *WDDMDevice::Lock(D3DKMT_HANDLE handle) {
D3DKMT_LOCK2 args = {0};
args.hDevice = device_;
args.hAllocation = handle;
NTSTATUS ret = D3DKMTLock2(&args);
if (ret == STATUS_SUCCESS)
return args.pData;
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return NULL;
}
bool WDDMDevice::Unlock(D3DKMT_HANDLE handle) {
D3DKMT_UNLOCK2 args = {0};
args.hDevice = device_;
args.hAllocation = handle;
NTSTATUS ret = D3DKMTUnlock2(&args);
if (ret == STATUS_SUCCESS)
return true;
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::CreateContext(int engine, D3DKMT_HANDLE *handle) {
void *priv_data;
int priv_size;
int ordinal = EngineOrdinal(engine, &device_info_);
if (ordinal < 0)
return false;
bool FwManagedGfxState = SupportStateShadowingByCpFw();
priv_size = rocr_proxy::CreateContextPrivData(&priv_data, FwManagedGfxState);
D3DKMT_CREATECONTEXTVIRTUAL args = {0};
args.hDevice = device_;
args.EngineAffinity = 1 << 0;
args.NodeOrdinal = ordinal;
args.pPrivateDriverData = priv_data;
args.PrivateDriverDataSize = priv_size;
args.ClientHint = D3DKMT_CLIENTHINT_OPENCL;
if (IsHwsEnabled(engine))
args.Flags.HwQueueSupported = 1;
else
args.Flags.DisableGpuTimeout = rocr_proxy::ShouldDisableGpuTimeout(engine, &device_info_);
NTSTATUS ret = D3DKMTCreateContextVirtual(&args);
if (ret == STATUS_SUCCESS) {
*handle = args.hContext;
rocr_proxy::DestroyPrivData(priv_data);
return true;
}
rocr_proxy::DestroyPrivData(priv_data);
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::DestroyContext(D3DKMT_HANDLE handle) {
D3DKMT_DESTROYCONTEXT args = {0};
args.hContext = handle;
NTSTATUS ret = D3DKMTDestroyContext(&args);
if (ret == STATUS_SUCCESS)
return true;
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::GpuWait(WDDMQueue *queue, const D3DKMT_HANDLE *syncobjs,
uint64_t *values, int count) {
D3DKMT_WAITFORSYNCHRONIZATIONOBJECTFROMGPU args = {0};
args.hContext = queue->context;
args.ObjectCount = count;
args.ObjectHandleArray = syncobjs;
args.MonitoredFenceValueArray = values;
NTSTATUS ret = D3DKMTWaitForSynchronizationObjectFromGpu(&args);
if (ret == STATUS_SUCCESS)
return true;
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::GpuSignal(D3DKMT_HANDLE context, const D3DKMT_HANDLE *syncobjs,
uint64_t *value, int count) {
D3DKMT_SIGNALSYNCHRONIZATIONOBJECTFROMGPU args = {0};
args.hContext = context;
args.ObjectCount = count;
args.ObjectHandleArray = syncobjs;
args.MonitoredFenceValueArray = value;
NTSTATUS ret = D3DKMTSignalSynchronizationObjectFromGpu(&args);
if (ret == STATUS_SUCCESS)
return true;
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::CpuWait(const D3DKMT_HANDLE *syncobjs, uint64_t *value,
int count, bool wait_any) {
D3DKMT_WAITFORSYNCHRONIZATIONOBJECTFROMCPU args = {0};
args.hDevice = device_;
args.ObjectCount = count;
args.ObjectHandleArray = syncobjs;
args.FenceValueArray = value;
args.Flags.WaitAny = wait_any;
NTSTATUS ret = D3DKMTWaitForSynchronizationObjectFromCpu(&args);
if (ret == STATUS_SUCCESS)
return true;
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
bool WDDMDevice::WaitOnPagingFenceFromCpu() {
uint64_t page_fence_value = 0;
page_fence_value = page_fence_value_.load();
if (CpuWait(&page_syncobj_, &page_fence_value, 1, false))
return true;
return false;
}
bool WDDMDevice::CreateSyncobj(D3DKMT_HANDLE *handle, uint64_t **addr) {
D3DKMT_CREATESYNCHRONIZATIONOBJECT2 args = {0};
args.hDevice = device_;
args.Info.Type = D3DDDI_MONITORED_FENCE;
args.Info.MonitoredFence.EngineAffinity = 1 << 0;
NTSTATUS ret = D3DKMTCreateSynchronizationObject2(&args);
if (ret == STATUS_SUCCESS) {
*handle = args.hSyncObject;
*addr = (uint64_t *)args.Info.MonitoredFence.FenceValueCPUVirtualAddress;
debug_print("create syncobj cpu addr=%p gpu addr=%" PRIx64 "\n",
args.Info.MonitoredFence.FenceValueCPUVirtualAddress,
args.Info.MonitoredFence.FenceValueGPUVirtualAddress);
return true;
}
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
void WDDMDevice::DestroySyncobj(D3DKMT_HANDLE handle) {
D3DKMT_DESTROYSYNCHRONIZATIONOBJECT args = {0};
args.hSyncObject = handle;
NTSTATUS ret = D3DKMTDestroySynchronizationObject(&args);
if (ret != STATUS_SUCCESS)
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
}
void WDDMDevice::InitCmdbufInfo(void) {
if (device_info_.major == 9) {
cmdbuf_aql_frame_size_ = 2 * sizeof(gfx9::AcquireMemTemplate);
} else if (device_info_.major >= 10) {
cmdbuf_aql_frame_size_ = 2 * sizeof(gfx10::AcquireMemTemplate);
}
if (device_info_.major >= 11)
cmdbuf_aql_frame_size_ += sizeof(SetScratchTemplate);
cmdbuf_aql_frame_size_ +=
sizeof(PM4MEC_COPY_DATA) * 2 +
sizeof(BarrierTemplate) * 2 +
sizeof(DispatchTemplate) +
sizeof(AtomicTemplate) * 2;
cmdbuf_aql_frame_size_ = AlignUp(cmdbuf_aql_frame_size_, 0x10);
cmdbuf_size_ = AlignUp(cmdbuf_aql_frame_num_ * cmdbuf_aql_frame_size_, 0x1000);
}
uint32_t WDDMDevice::LdsBlocks(const hsa_kernel_dispatch_packet_t *pkt) {
static const uint32_t blk_sz = 512;
uint32_t total_sz = pkt->group_segment_size;
uint32_t blk_num = (total_sz + blk_sz - 1) / blk_sz;
return blk_num;
}
NTSTATUS WDDMGetAdapters(D3DKMT_ADAPTERINFO *&adapters, int &num_adapters)
{
bool supported = false;
D3DKMT_ENUMADAPTERS2 args = {0};
NTSTATUS ret = D3DKMTEnumAdapters2(&args);
if (ret != STATUS_SUCCESS)
return ret;
if (!args.NumAdapters) {
adapters = NULL;
num_adapters = 0;
return STATUS_SUCCESS;
}
D3DKMT_ADAPTERINFO *info = new D3DKMT_ADAPTERINFO[args.NumAdapters];
if (!info)
return STATUS_NO_MEMORY;
args.pAdapters = info;
ret = D3DKMTEnumAdapters2(&args);
if (ret != STATUS_SUCCESS)
goto err_out0;
adapters = new D3DKMT_ADAPTERINFO[args.NumAdapters];
if (!adapters)
goto err_out0;
num_adapters = 0;
for (int i = 0; i < args.NumAdapters; i++) {
D3DKMT_ADAPTERREGISTRYINFO query = {0};
ret = WDDMQueryAdapter(info[i].hAdapter, KMTQAITYPE_ADAPTERREGISTRYINFO,
&query, sizeof(query));
if (ret != STATUS_SUCCESS)
goto err_out1;
supported = rocr_proxy::QueryAdapterSupported(info[i].hAdapter);
if (std::wcsstr(query.ChipType, L"AMD") && supported) {
adapters[num_adapters++] = info[i];
}
}
delete info;
return STATUS_SUCCESS;
err_out1:
delete adapters;
adapters = NULL;
err_out0:
delete info;
return ret;
}
bool WDDMDevice::ParseDeviceInfo() {
bool ret;
memset(&device_info_, 0, sizeof(device_info_));
ret = rocr_proxy::ParseAdapterInfo(adapter_, &device_info_);
if (!ret)
return false;
return true;
}
void WDDMDevice::DestroyDeviceInfo() {
free(device_info_.adapter_info);
}
void WDDMDevice::GetClockCounters(uint64_t *gpu, uint64_t *cpu) {
void *priv_data;
int priv_size;
priv_size = rocr_proxy::CreateCalibratedTimestampsPrivData(&priv_data);
D3DKMT_ESCAPE d3dkmt_escape;
memset(&d3dkmt_escape, 0, sizeof(d3dkmt_escape));
d3dkmt_escape.hAdapter = adapter_;
d3dkmt_escape.hDevice = device_;
d3dkmt_escape.hContext = 0; //KMD only use device to identify the process
d3dkmt_escape.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
d3dkmt_escape.pPrivateDriverData = priv_data;
d3dkmt_escape.PrivateDriverDataSize = priv_size;
d3dkmt_escape.Flags.HardwareAccess = true;
NTSTATUS status = D3DKMTEscape(&d3dkmt_escape);
if (status) {
debug_print("%s status %d \n", __FUNCTION__, status);
} else {
rocr_proxy::QueryCalibratedTimestamps(priv_data, gpu, cpu);
}
rocr_proxy::DestroyPrivData(priv_data);
}
bool WDDMDevice::CreateQueue(WDDMQueue *queue) {
if (!CreateContext(queue->queue_engine, &queue->context))
return false;
GpuMemoryCreateInfo create_info{};
create_info.size = queue->cmdbuf_size;
create_info.domain = rocr_proxy::kSystem;
GpuMemory *gpu_mem = nullptr;
auto code = CreateGpuMemory(create_info, &gpu_mem);
if (code != ErrorCode::Success)
goto err_out0;
queue->cmdbuf = gpu_mem->GetGpuMemoryHandle();
queue->cmdbuf_addr = gpu_mem->GpuAddress();
if (queue->Init())
goto err_out1;
return true;
err_out1:
delete gpu_mem;
err_out0:
DestroyContext(queue->context);
return false;
}
void WDDMDevice::DestroyQueue(WDDMQueue *queue) {
queue->Fini();
auto cmdbuf_mem = GpuMemory::Convert(queue->cmdbuf);
delete cmdbuf_mem;
DestroyContext(queue->context);
}
bool WDDMDevice::SubmitToSwQueue(WDDMQueue *queue, uint64_t command_addr,
uint64_t command_size, uint64_t fence_value) {
void *priv_data;
int priv_size;
priv_size = rocr_proxy::CreateSubmitPrivData(&priv_data, queue->queue, command_addr, command_size, false);
D3DKMT_SUBMITCOMMAND args = {0};
args.Commands = command_addr;
args.CommandLength = command_size;
args.BroadcastContextCount = 1;
args.BroadcastContext[0] = queue->context;
args.pPrivateDriverData = priv_data;
args.PrivateDriverDataSize = priv_size;
NTSTATUS ret = D3DKMTSubmitCommand(&args);
if (ret != STATUS_SUCCESS) {
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
rocr_proxy::DestroyPrivData(priv_data);
return false;
}
rocr_proxy::DestroyPrivData(priv_data);
if (!GpuSignal(queue->context, &queue->syncobj, &fence_value, 1))
return false;
return true;
}
bool WDDMDevice::CreateHwQueue(WDDMQueue *queue) {
void *priv_data;
int priv_size;
bool FwManagedGfxState = SupportStateShadowingByCpFw();
priv_size = rocr_proxy::CreateHwQueuePrivData(&priv_data, queue->context,
FwManagedGfxState, queue->prio);
D3DKMT_CREATEHWQUEUE createHwQueue = {0};
createHwQueue.hHwContext = queue->context;
createHwQueue.Flags.DisableGpuTimeout = rocr_proxy::ShouldDisableGpuTimeout(queue->queue_engine, &device_info_);
createHwQueue.pPrivateDriverData = priv_data;
createHwQueue.PrivateDriverDataSize = priv_size;
NTSTATUS ret = D3DKMTCreateHwQueue(&createHwQueue);
if (ret != STATUS_SUCCESS) {
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
rocr_proxy::DestroyPrivData(priv_data);
return false;
}
rocr_proxy::DestroyPrivData(priv_data);
queue->queue = createHwQueue.hHwQueue;
queue->syncobj = createHwQueue.hHwQueueProgressFence;
queue->sync_addr = (uint64_t *)createHwQueue.HwQueueProgressFenceCPUVirtualAddress;
return true;
}
bool WDDMDevice::DestroyHwQueue(WDDMQueue *queue) {
D3DKMT_DESTROYHWQUEUE DestroyHwQueue = {
.hHwQueue = queue->queue,
};
NTSTATUS ret = D3DKMTDestroyHwQueue(&DestroyHwQueue);
if (ret != STATUS_SUCCESS) {
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
return false;
}
return true;
}
bool WDDMDevice::SubmitToHwQueue(WDDMQueue *queue, uint64_t command_addr,
uint64_t command_size, uint64_t fence_value) {
void *priv_data;
int priv_size;
priv_size = rocr_proxy::CreateSubmitPrivData(&priv_data, queue->queue, command_addr, command_size, true);
D3DKMT_SUBMITCOMMANDTOHWQUEUE args = {0};
args.hHwQueue = queue->queue;
args.HwQueueProgressFenceId = fence_value;
args.CommandBuffer = command_addr;
args.CommandLength = command_size;
args.pPrivateDriverData = priv_data;
args.PrivateDriverDataSize = priv_size;
NTSTATUS ret = D3DKMTSubmitCommandToHwQueue(&args);
if (ret != STATUS_SUCCESS) {
fprintf(stderr, "%s fail %x\n", __FUNCTION__, ret);
rocr_proxy::DestroyPrivData(priv_data);
return false;
}
rocr_proxy::DestroyPrivData(priv_data);
return true;
}
} // namespace core
} // namespace rocr
+467
Parādīt failu
@@ -0,0 +1,467 @@
#include <cinttypes>
#include <cassert>
#include "inc/wddm/gpu_memory.h"
#include "inc/wddm/device.h"
#include "util/utils.h"
using namespace std;
namespace rocr {
namespace core {
size_t GpuMemory::CalcChunkNumbers(gpusize size) {
const auto chunk_size = core::WDDMDevice::GpuMemoryChunkSize;
return (size + chunk_size - 1) / chunk_size;
}
gpusize GpuMemory::AdjustSize(gpusize size) const {
const auto &device_info = device_->DeviceInfo();
if (device_info.enable_big_page_alignment && desc_.domain == rocr_proxy::kLocal) {
uint32_t alignment = device_info.big_page_alignment_size;
// BigPage is only supported for allocations > bigPageMinAlignment.
// Also, if bigPageMinAlignment == 0, BigPage optimization is not supported per KMD.
// We do either LargePage or BigPage alignment, whichever has a higher value.
if ((device_info.hw_big_page_min_alignment_size > 0) && (size > device_info.hw_big_page_min_alignment_size)) {
alignment = std::max(alignment, device_info.hw_big_page_min_alignment_size);
if (size > device_info.hw_big_page_alignment_size)
alignment = std::max(alignment, device_info.hw_big_page_alignment_size);
}
if (alignment > 0)
size = AlignUp(size, alignment);
} else {
const size_t min_size = 4096;
size = AlignUp(size, min_size);
}
return size;
}
GpuMemory::GpuMemory(WDDMDevice *device) : device_(device) {
num_allocations_ = 0;
alloc_handles_ptr_ = nullptr;
alloc_handle_ = 0;
resource_ = 0;
}
GpuMemory::~GpuMemory() {
FreeGpuVirtualAddress(GpuAddress(), Size());
FreePhysicalMemory();
}
ErrorCode GpuMemory::Init(const GpuMemoryCreateInfo &create_info) {
desc_.domain = create_info.domain;
desc_.adapter_luid = device_->GetLuid();
desc_.client_size = create_info.size;
desc_.alignment = create_info.alignment;
desc_.mem_flags = create_info.mem_flags;
desc_.engine_flag = create_info.engine_flag;
desc_.flags.is_virtual = create_info.flags.virtual_alloc;
desc_.flags.is_physical_only = create_info.flags.physical_only;
desc_.flags.is_shared = create_info.flags.interprocess;
desc_.flags.is_locked = create_info.flags.locked;
desc_.size = AdjustSize(desc_.client_size);
if (IsUserMemory() || IsSystem())
desc_.cpu_addr = create_info.user_ptr;
num_allocations_ = CalcChunkNumbers(Size());
if (num_allocations_ == 1)
alloc_handles_ptr_ = &alloc_handle_;
else
alloc_handles_ptr_ = new WinAllocationHandle[num_allocations_];
memset(alloc_handles_ptr_, 0, num_allocations_ * sizeof(WinAllocationHandle));
auto code = ErrorCode::Success;
if (IsPhysicalOnly()) {
code = CreatePhysicalMemory();
return code;
}
code = ReserveGpuVirtualAddress(create_info.va_hint, Size(), create_info.alignment);
if (IsVirtual() || (code != ErrorCode::Success))
return code;
bool physical_created = false;
auto guard = MakeScopeGuard([this, &physical_created, &code]() {
if (code != ErrorCode::Success) {
if (physical_created) {
FreePhysicalMemory();
}
FreeGpuVirtualAddress(GpuAddress(), Size());
}
});
(void)guard;
code = CreatePhysicalMemory();
if (code != ErrorCode::Success)
return code;
physical_created = true;
code = MapGpuVirtualAddress(GpuAddress(), Size());
if (code != ErrorCode::Success)
return code;
code = MakeResident();
if (code != ErrorCode::Success)
return code;
if (!GetDevice()->WaitOnPagingFenceFromCpu())
code = ErrorCode::Unknown;
return code;
}
ErrorCode GpuMemory::UnmapGpuVirtualAddress(const gpusize addr, const gpusize size, gpusize offset) {
auto code = ErrorCode::Success;
size_t i = 0;
auto map_addr = addr;
auto map_size = size;
while (offset >= core::WDDMDevice::GpuMemoryChunkSize) {
offset -= core::WDDMDevice::GpuMemoryChunkSize;
i += 1;
}
while (map_size > 0) {
auto block_size = std::min(map_size, core::WDDMDevice::GpuMemoryChunkSize);
D3DDDI_MAPGPUVIRTUALADDRESS args{};
args.hPagingQueue = device_->PagingQueue();
args.BaseAddress = map_addr;
args.hAllocation = GetAllocationHandle(i);
args.SizeInPages = block_size / 0x1000;
args.Protection.NoAccess = 1;
code = thunk::MapGpuVirtualAddress(&args);
if (code == ErrorCode::NotReady)
device_->UpdatePageFence(args.PagingFenceValue);
else if (code != ErrorCode::Success)
break;
map_addr += block_size;
map_size -= block_size;
offset = 0; // reset second unmapped allocation offset to zero
i += 1;
}
return code;
}
ErrorCode GpuMemory::MapGpuVirtualAddress(const gpusize addr, const gpusize size, gpusize offset) {
auto code = ErrorCode::Success;
size_t i = 0;
auto map_addr = addr;
auto map_size = size;
const size_t _4K = 0x1000;
while (offset >= core::WDDMDevice::GpuMemoryChunkSize) {
offset -= core::WDDMDevice::GpuMemoryChunkSize;
i += 1;
}
const size_t first_chunk = i;
const auto first_chunk_offset = offset;
/* Found two limitation for local vram:
* 1. invisible vram va has to be 64K aligned, otherwise map gpu va fail
* 2. visible vram can not be cpu mapped when command submission or after gpu mapped
*/
while (map_size > 0) {
auto block_size = std::min(map_size, core::WDDMDevice::GpuMemoryChunkSize);
D3DDDI_MAPGPUVIRTUALADDRESS args{};
args.hPagingQueue = device_->PagingQueue();
args.BaseAddress = map_addr;
args.hAllocation = GetAllocationHandle(i);
args.OffsetInPages = offset / _4K;
args.SizeInPages = block_size / _4K;
args.Protection.Write = 1;
code = thunk::MapGpuVirtualAddress(&args);
if (code != ErrorCode::Success) {
if (code == ErrorCode::NotReady) {
const uint64_t fence_value = args.PagingFenceValue;
device_->UpdatePageFence(fence_value);
code = ErrorCode::Success;
} else
break;
}
map_addr += block_size;
map_size -= block_size;
offset = 0; // reset second mapped allocation offset to zero
i++;
}
if (code != ErrorCode::Success) {
// Map failed, unmap partial mapped block
offset = first_chunk_offset;
map_addr = addr;
map_size = size;
for (size_t j = first_chunk; j < i; j++) {
auto block_size = std::min(map_size, core::WDDMDevice::GpuMemoryChunkSize);
D3DDDI_MAPGPUVIRTUALADDRESS args{};
args.hPagingQueue = device_->PagingQueue();
args.BaseAddress = map_addr;
args.hAllocation = 0;
args.OffsetInPages = offset / _4K;
args.SizeInPages = block_size / _4K;
args.Protection.NoAccess = 1;
auto unmap_code = thunk::MapGpuVirtualAddress(&args);
if (unmap_code == ErrorCode::NotReady)
device_->UpdatePageFence(args.PagingFenceValue);
map_addr += block_size;
map_size -= block_size;
}
}
return code;
}
ErrorCode GpuMemory::ReserveGpuVirtualAddress(gpusize base_virt_addr, gpusize size, gpusize alignment) {
gpusize gpu_virt_addr = 0;
auto status = device_->ReserveGpuVirtualAddress(desc_.domain, base_virt_addr, size, &gpu_virt_addr, alignment,
desc_.flags.is_locked);
if (status == ErrorCode::Success) {
desc_.gpu_addr = gpu_virt_addr;
if (IsSystem())
desc_.cpu_addr = reinterpret_cast<void *>(desc_.gpu_addr);
}
return status;
}
ErrorCode GpuMemory::FreeGpuVirtualAddress(gpusize base_addr, gpusize size) {
return base_addr != 0 ?
device_->FreeGpuVirtualAddress(desc_.domain, base_addr, size) :
ErrorCode::Success;
}
ErrorCode GpuMemory::CreatePhysicalMemory() {
assert(!IsVirtual() && NumChunks() > 0);
const auto num_allocations = NumChunks();
void *priv_drv_data;
void *alloc_priv;
int priv_drv_data_size;
int alloc_priv_data_size;
if (!rocr_proxy::CreatePrivateAllocInfo(NumChunks(), &priv_drv_data, &alloc_priv,
&priv_drv_data_size, &alloc_priv_data_size))
return ErrorCode::OutOfMemory;
auto alloc_info = reinterpret_cast<D3DDDI_ALLOCATIONINFO2*>(
static_cast<unsigned char*>(priv_drv_data) + priv_drv_data_size * num_allocations);
size_t size = desc_.size;
uint64_t addr = desc_.gpu_addr;
char *cpu_addr = static_cast<char *>(desc_.cpu_addr);
const auto &device_info = GetDevice()->DeviceInfo();
for (size_t i = 0; i < num_allocations; i++) {
void* priv_data = (void*)((char*)priv_drv_data + priv_drv_data_size * i);
size_t block_size = std::min(size, core::WDDMDevice::GpuMemoryChunkSize);
if (IsUserMemory() || IsSystem()) {
rocr_proxy::SetAllocationInfo(priv_data, block_size, desc_.domain, 0, desc_.mem_flags, desc_.engine_flag, device_info);
alloc_info[i].pSystemMem = static_cast<void *>(cpu_addr);
cpu_addr += block_size;
} else {
rocr_proxy::SetAllocationInfo(priv_data, block_size, desc_.domain, addr, desc_.mem_flags, desc_.engine_flag, device_info);
}
size -= block_size;
addr += block_size;
alloc_info[i].pPrivateDriverData = priv_data;
alloc_info[i].PrivateDriverDataSize = priv_drv_data_size;
alloc_info[i].VidPnSourceId = D3DDDI_ID_UNINITIALIZED;
}
D3DKMT_CREATEALLOCATION args = {};
args.hDevice = device_->DeviceHandle();
args.pPrivateDriverData = alloc_priv;
args.PrivateDriverDataSize = alloc_priv_data_size;
args.NumAllocations = num_allocations;
args.pAllocationInfo2 = alloc_info;
SharedHandleInfo shared_info;
if (IsShared()) {
shared_info.size = desc_.size;
shared_info.client_size = desc_.client_size;
shared_info.domain = desc_.domain;
shared_info.adapter_luid = desc_.adapter_luid;
shared_info.flags = reinterpret_cast<uint32_t>(desc_.flags.reserved);
shared_info.mem_flags = desc_.mem_flags;
args.pPrivateRuntimeData = &shared_info;
args.PrivateRuntimeDataSize = sizeof(shared_info);
args.Flags.NtSecuritySharing = 1;
args.Flags.CreateShared = 1;
args.Flags.CreateResource = 1;
}
auto status = thunk::CreateAllocation(&args);
if (status == ErrorCode::Success) {
for (size_t i = 0; i < num_allocations; i++)
alloc_handles_ptr_[i] = alloc_info[i].hAllocation;
resource_ = args.hResource;
}
rocr_proxy::DestroyPrivateAllocInfo(priv_drv_data, alloc_priv);
return status;
}
ErrorCode GpuMemory::FreePhysicalMemory() {
auto code = ErrorCode::Success;
if (alloc_handles_ptr_ == nullptr || (NumChunks() == 1 && *alloc_handles_ptr_ == 0))
return code;
code = thunk::DestroyAllocation(device_->DeviceHandle(),
resource_,
NumChunks(),
alloc_handles_ptr_);
if (NumChunks() > 1)
delete[] alloc_handles_ptr_;
alloc_handles_ptr_ = nullptr;
return code;
}
ErrorCode GpuMemory::MakeResident() {
D3DDDI_MAKERESIDENT args = {};
args.hPagingQueue = device_->PagingQueue();
args.NumAllocations = NumChunks();
args.AllocationList = alloc_handles_ptr_;
args.Flags.CantTrimFurther = 1;
auto code = thunk::MakeResident(&args);
if (code == ErrorCode::NotReady) {
const auto fence_value = args.PagingFenceValue;
device_->UpdatePageFence(fence_value);
code = ErrorCode::Success;
}
return code;
}
ErrorCode GpuMemory::Evict() {
D3DKMT_EVICT args = {};
args.hDevice = device_->DeviceHandle();
args.NumAllocations = NumChunks();
args.AllocationList = alloc_handles_ptr_;
return thunk::Evict(&args);
}
ErrorCode GpuMemory::ExportPhysicalHandle(int* dmabuf_fd, uint32_t flags) {
if (IsShared())
return thunk::ShareObjects(num_allocations_, resource_, flags, dmabuf_fd);
else
return ErrorCode::UnSupported;
}
ErrorCode GpuMemory::ImportPhysicalHandle(int dmabuf_fd) {
D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE query_args;
if (dmabuf_fd <= 0)
return ErrorCode::InvalidateParams;
memset(&query_args, 0, sizeof(query_args));
query_args.hDevice = device_->DeviceHandle();
query_args.hNtHandle = reinterpret_cast<HANDLE>(dmabuf_fd);
auto ret = thunk::QueryResourceInfoFromNtHandle(&query_args);
if (ret != ErrorCode::Success) {
debug_print("%s query resource info from nt handle failed %d\n", __FUNCTION__, static_cast<int>(ret));
return ErrorCode::InvalidateParams;
}
debug_print("wsl-rocr: import from nt handle %d, get allocation number %d,"
" runtime data size %" PRIx64 " total driver data size %" PRIx64 "resource data size=%" PRIx64 "\n",
dmabuf_fd,
query_args.NumAllocations,
query_args.PrivateRuntimeDataSize,
query_args.TotalPrivateDriverDataSize,
query_args.ResourcePrivateDriverDataSize);
SharedHandleInfo shared_info;
if(sizeof(shared_info) != query_args.PrivateRuntimeDataSize) {
debug_print("%s shared hanle info size mismatch:%d vs %ld\n",
__FUNCTION__, query_args.PrivateRuntimeDataSize, sizeof(shared_info));
return ErrorCode::UnSupported;
}
uint32_t total_size = query_args.NumAllocations * sizeof(D3DDDI_OPENALLOCATIONINFO2) +
query_args.TotalPrivateDriverDataSize +
query_args.ResourcePrivateDriverDataSize;
D3DDDI_OPENALLOCATIONINFO2 *open_info =
reinterpret_cast<D3DDDI_OPENALLOCATIONINFO2*> (calloc(1, total_size));
if (!open_info) {
debug_print("%s alloc open_info failed, NumAllocations:%d\n",
__FUNCTION__, query_args.NumAllocations);
return ErrorCode::OutOfMemory;
}
alloc_handles_ptr_ = new WinAllocationHandle[query_args.NumAllocations];
D3DKMT_OPENRESOURCEFROMNTHANDLE open_args;
memset(&open_args, 0, sizeof(open_args));
open_args.hDevice = query_args.hDevice;
open_args.hNtHandle = query_args.hNtHandle;
open_args.NumAllocations = query_args.NumAllocations;
open_args.pOpenAllocationInfo2 = open_info;
open_args.TotalPrivateDriverDataBufferSize = query_args.TotalPrivateDriverDataSize;
open_args.pTotalPrivateDriverDataBuffer = reinterpret_cast<void*>
(open_args.pOpenAllocationInfo2 + open_args.NumAllocations);
open_args.ResourcePrivateDriverDataSize = query_args.ResourcePrivateDriverDataSize;
open_args.pResourcePrivateDriverData = reinterpret_cast<void*>
(((uint64_t)open_args.pTotalPrivateDriverDataBuffer) +
open_args.TotalPrivateDriverDataBufferSize);
open_args.PrivateRuntimeDataSize = query_args.PrivateRuntimeDataSize;
open_args.pPrivateRuntimeData = reinterpret_cast<void*> (&shared_info);
ret = thunk::OpenResourceFromNtHandle(&open_args);
if (ret != ErrorCode::Success) {
ret = ErrorCode::InvalidateParams;
debug_print("%s open resource failed %d\n", __FUNCTION__, static_cast<int>(ret));
goto err_out;
}
desc_.size = shared_info.size;
desc_.client_size = shared_info.client_size;
desc_.domain = shared_info.domain;
desc_.flags.reserved = shared_info.flags;
desc_.mem_flags = shared_info.mem_flags;
desc_.adapter_luid = shared_info.adapter_luid;
resource_ = open_args.hResource;
num_allocations_ = open_args.NumAllocations;
for (int i = 0; i < num_allocations_; i++)
alloc_handles_ptr_[i] = open_info[i].hAllocation;
free(open_info);
return ErrorCode::Success;
err_out:
delete[] alloc_handles_ptr_;
alloc_handles_ptr_ = nullptr;
free(open_info);
return ret;
}
} // namespace code
} // namespace rocr
+989
Parādīt failu
@@ -0,0 +1,989 @@
////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
//
// Developed by:
//
// AMD Research and AMD HSA Software Development
//
// Advanced Micro Devices, Inc.
//
// www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal with 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:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in
// the documentation and/or other materials provided with the distribution.
// - Neither the names of Advanced Micro Devices, Inc,
// nor the names of its contributors may be used to endorse or promote
// products derived from this Software without specific prior written
// permission.
//
// 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
#include <cstring>
#include <cinttypes>
#include <cstddef>
#include "inc/wddm/queue.h"
#include "inc/registers.h"
#include "libhsakmt.h"
namespace rocr {
namespace core {
hsa_status_t WDDMQueue::SwsInit(void) {
if (!device->CreateSyncobj(&syncobj, &sync_addr))
return HSA_STATUS_ERROR;
if (device->AllocUserQueueMemFromUMD()) {
GpuMemory *gpu_mem = nullptr;
GpuMemoryCreateInfo create_info{};
create_info.domain = rocr_proxy::kUserQueue;
create_info.size = device->GetSwsQueueSize();
create_info.engine_flag = rocr_proxy::QueueEngine2EngineFlag(queue_engine);
auto code = device->CreateGpuMemory(create_info, &gpu_mem);
if (code != ErrorCode::Success) {
device->DestroySyncobj(syncobj);
return HSA_STATUS_ERROR;
}
queue_mem = gpu_mem->GetGpuMemoryHandle();
queue = gpu_mem->GetAllocationHandle(0);
}
return HSA_STATUS_SUCCESS;
}
hsa_status_t WDDMQueue::SwsFini(void) {
device->DestroySyncobj(syncobj);
return HSA_STATUS_SUCCESS;
}
hsa_status_t WDDMQueue::SwsSubmit(uint64_t command_addr,
uint64_t command_size,
uint64_t fence_value) {
if (!device->SubmitToSwQueue(this, command_addr, command_size, fence_value))
return HSA_STATUS_ERROR;
return HSA_STATUS_SUCCESS;
}
hsa_status_t WDDMQueue::HwsInit(void) {
if (!device->CreateHwQueue(this))
return HSA_STATUS_ERROR;
return HSA_STATUS_SUCCESS;
}
hsa_status_t WDDMQueue::HwsFini(void) {
if (!device->DestroyHwQueue(this))
return HSA_STATUS_ERROR;
return HSA_STATUS_SUCCESS;
}
hsa_status_t WDDMQueue::HwsSubmit(uint64_t command_addr,
uint64_t command_size,
uint64_t fence_value) {
if (!device->SubmitToHwQueue(this, command_addr, command_size, fence_value))
return HSA_STATUS_ERROR;
return HSA_STATUS_SUCCESS;
}
hsa_status_t WDDMQueue::SetPriority(hsa_amd_queue_priority_t priority) {
if (!use_hws)
return HSA_STATUS_SUCCESS;
rocr_proxy::SchedLevel new_prio = ConvertSchedLevel(priority);
if (prio == new_prio)
return HSA_STATUS_SUCCESS;
debug_print("set prio %d -> %d\n", prio, new_prio);
device->DestroyHwQueue(this);
prio = new_prio;
return HwsInit();
}
extern "C" void (*fn_hsa_signal_store_screlease)(hsa_signal_t hsa_signal, hsa_signal_value_t value);
void ComputeQueue::HandleError(hsa_status_t status) {
hsa_signal_t sig = amd_queue_rocr_->queue_inactive_signal;
hsa_signal_value_t val = -1;
struct queue_error_t {
uint32_t code;
hsa_status_t status;
};
static const queue_error_t QueueErrors[] = {
{2, HSA_STATUS_ERROR_INCOMPATIBLE_ARGUMENTS},
{4, HSA_STATUS_ERROR_INVALID_ALLOCATION},
{8, HSA_STATUS_ERROR_INVALID_CODE_OBJECT},
//{16, HSA_STATUS_ERROR_INVALID_ARGUMENT},
{32, HSA_STATUS_ERROR_INVALID_PACKET_FORMAT},
{64, HSA_STATUS_ERROR_INVALID_ARGUMENT},
//{128, HSA_STATUS_ERROR_OUT_OF_REGISTERS},
//{0x20000000, HSA_STATUS_ERROR_MEMORY_APERTURE_VIOLATION},
//{0x40000000, HSA_STATUS_ERROR_ILLEGAL_INSTRUCTION},
{0x80000000, HSA_STATUS_ERROR_EXCEPTION},
};
for (std::size_t i = 0; i < sizeof(QueueErrors) / sizeof(QueueErrors[0]); ++i) {
if (QueueErrors[i].status == status) {
val = QueueErrors[i].code;
debug_print("error %d, sig_val %d\n", status, val);
break;
}
}
if (sig.handle) {
fn_hsa_signal_store_screlease(sig, val);
}
if (error_code_) {
error_code_->store(val, std::memory_order_release);
}
}
void ComputeQueue::AqlToPm4Thread(ComputeQueue *queue) {
ComputeQueue *cq = queue;
// This timing system is used for sleeping this Thread
// when one packet is invalid for about 2 seconds.
std::chrono::steady_clock::time_point start_time, time;
// Set the polling timeout value for 2 seconds
const std::chrono::milliseconds kMaxElapsed(2000);
uint64_t current_position = cq->GetAqlWriteIndex();
bool sleep = false;
start_time = std::chrono::steady_clock::now();
while (true) {
if (!cq->IsInvalidPacket()) {
hsa_status_t status = cq->Process();
if (status != HSA_STATUS_SUCCESS) {
fprintf(stderr, "process compute queue fail status = %08x\n", status);
queue->HandleError(status);
break;
}
}
std::unique_lock<std::mutex> lock(queue->thread_cond_lock_);
if (current_position == cq->GetAqlWriteIndex()) {
time = std::chrono::steady_clock::now();
if (time - start_time > kMaxElapsed)
sleep = true;
} else {
start_time = std::chrono::steady_clock::now();
current_position = cq->GetAqlWriteIndex();
sleep = false;
}
// CPU wait for valid packet
if (cq->GetRingWptr()->load() <= cq->GetRingRptr()->load() ||
(sleep && cq->IsInvalidPacket())) {
if (queue->thread_stop_)
break;
debug_print("wait %p wptr=%" PRIx64 " rptr=%" PRIx64 "\n",
queue->ring, cq->GetRingWptr()->load(), cq->GetRingRptr()->load());
queue->thread_cond_.wait(lock);
}
}
debug_print("aql to pm4 thread %p exit\n", queue->ring);
}
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) :
WDDMQueue(device, cmdbuf_size, engine, use_hws),
ring(ring),
ring_size(ring_size),
ring_wptr(ring_wptr),
ring_rptr(ring_rptr),
error_code_(reinterpret_cast<volatile std::atomic<long int>*>(error_addr)),
ib_start_addr(0),
ib_size(0),
sync_point(0),
cmdbuf_aql_frame_write_index(0),
cmdbuf_aql_frame_size(0),
needs_barrier(true),
ready_to_submit(false),
thread_stop_(false),
scratch_waves_(device->MaxScratchSlotsPerCu() * device->ComputeUnitCount()),
scratch_size_per_wave_(0),
scratch_size_(0),
scratch_base_(nullptr) {
bool ret = device->CreateQueue(this);
assert(ret);
GpuMemoryCreateInfo create_info{};
create_info.size = PAGE_SIZE;
create_info.domain = rocr_proxy::kSystem;
GpuMemory *gpu_mem = nullptr;
auto code = device->CreateGpuMemory(create_info, &gpu_mem);
assert(code == ErrorCode::Success);
amd_queue_mem_ = gpu_mem->GetGpuMemoryHandle();
amd_queue_ = reinterpret_cast<amd_queue_t*>(gpu_mem->GpuAddress());
aql_to_pm4_thread_ = std::thread(AqlToPm4Thread, this);
amd_queue_rocr_ = (amd_queue_t*)((char*)ring_rptr - offsetof(amd_queue_t, read_dispatch_id));
}
ComputeQueue::~ComputeQueue() {
thread_cond_lock_.lock();
thread_stop_ = true;
thread_cond_lock_.unlock();
thread_cond_.notify_one();
aql_to_pm4_thread_.join();
//doorbell_signal_->Release();
device->DestroyQueue(this);
if (scratch_base_) {
auto scratch_gpu_mem = GpuMemory::Convert(scratch_mem_);
delete scratch_gpu_mem;
}
auto amd_queue_gpu_mem = GpuMemory::Convert(amd_queue_mem_);
delete amd_queue_gpu_mem;
}
void ComputeQueue::InitScratchSRD() {
// Populate scratch resource descriptor
SQ_BUF_RSRC_WORD0 srd0;
uintptr_t scratch_base = uintptr_t(scratch_base_);
srd0.bits.BASE_ADDRESS = scratch_base;
uint32_t srd1_u32;
if (device->Major() < 11) {
SQ_BUF_RSRC_WORD1 srd1;
srd1.bits.BASE_ADDRESS_HI = scratch_base >> 32;
srd1.bits.STRIDE = 0;
srd1.bits.CACHE_SWIZZLE = 0;
srd1.bits.SWIZZLE_ENABLE = 1;
srd1_u32 = srd1.u32All;
} else {
SQ_BUF_RSRC_WORD1_GFX11 srd1;
srd1.bits.BASE_ADDRESS_HI = scratch_base >> 32;
srd1.bits.STRIDE = 0;
srd1.bits.SWIZZLE_ENABLE = 1;
srd1_u32 = srd1.u32All;
}
SQ_BUF_RSRC_WORD2 srd2;
srd2.bits.NUM_RECORDS = scratch_size_;
uint32_t srd3_u32;
if (device->Major() < 10) {
SQ_BUF_RSRC_WORD3 srd3;
srd3.bits.DST_SEL_X = SQ_SEL_X;
srd3.bits.DST_SEL_Y = SQ_SEL_Y;
srd3.bits.DST_SEL_Z = SQ_SEL_Z;
srd3.bits.DST_SEL_W = SQ_SEL_W;
srd3.bits.NUM_FORMAT = BUF_NUM_FORMAT_UINT;
srd3.bits.DATA_FORMAT = BUF_DATA_FORMAT_32;
srd3.bits.ELEMENT_SIZE = 1; // 4
srd3.bits.INDEX_STRIDE = 3; // 64
srd3.bits.ADD_TID_ENABLE = 1;
srd3.bits.ATC__CI__VI = 0;
srd3.bits.HASH_ENABLE = 0;
srd3.bits.HEAP = 0;
srd3.bits.MTYPE__CI__VI = 0;
srd3.bits.TYPE = SQ_RSRC_BUF;
srd3_u32 = srd3.u32All;
} else if (device->Major() == 10) {
SQ_BUF_RSRC_WORD3_GFX10 srd3;
srd3.bits.DST_SEL_X = SQ_SEL_X;
srd3.bits.DST_SEL_Y = SQ_SEL_Y;
srd3.bits.DST_SEL_Z = SQ_SEL_Z;
srd3.bits.DST_SEL_W = SQ_SEL_W;
srd3.bits.FORMAT = BUF_FORMAT_32_UINT;
srd3.bits.RESERVED1 = 0;
srd3.bits.INDEX_STRIDE = 0; // filled in by CP
srd3.bits.ADD_TID_ENABLE = 1;
srd3.bits.RESOURCE_LEVEL = 1;
srd3.bits.RESERVED2 = 0;
srd3.bits.OOB_SELECT = 2; // no bounds check in swizzle mode
srd3.bits.TYPE = SQ_RSRC_BUF;
srd3_u32 = srd3.u32All;
} else if (device->Major() == 11) {
SQ_BUF_RSRC_WORD3_GFX11 srd3;
srd3.bits.DST_SEL_X = SQ_SEL_X;
srd3.bits.DST_SEL_Y = SQ_SEL_Y;
srd3.bits.DST_SEL_Z = SQ_SEL_Z;
srd3.bits.DST_SEL_W = SQ_SEL_W;
srd3.bits.FORMAT = BUF_FORMAT_32_UINT;
srd3.bits.RESERVED1 = 0;
srd3.bits.INDEX_STRIDE = 0; // filled in by CP
srd3.bits.ADD_TID_ENABLE = 1;
srd3.bits.RESERVED2 = 0;
srd3.bits.OOB_SELECT = 2; // no bounds check in swizzle mode
srd3.bits.TYPE = SQ_RSRC_BUF;
srd3_u32 = srd3.u32All;
} else {
SQ_BUF_RSRC_WORD3_GFX12 srd3;
srd3.bits.DST_SEL_X = SQ_SEL_X;
srd3.bits.DST_SEL_Y = SQ_SEL_Y;
srd3.bits.DST_SEL_Z = SQ_SEL_Z;
srd3.bits.DST_SEL_W = SQ_SEL_W;
srd3.bits.FORMAT = BUF_FORMAT_32_UINT;
srd3.bits.RESERVED1 = 0;
srd3.bits.INDEX_STRIDE = 0; // filled in by CP
srd3.bits.ADD_TID_ENABLE = 1;
srd3.bits.WRITE_COMPRESS_ENABLE = 0;
srd3.bits.COMPRESSION_EN = 0;
srd3.bits.COMPRESSION_ACCESS_MODE = 0;
srd3.bits.OOB_SELECT = 2; // no bounds check in swizzle mode
srd3.bits.TYPE = SQ_RSRC_BUF;
srd3_u32 = srd3.u32All;
}
// Update Queue's Scratch descriptor's property
amd_queue_->scratch_resource_descriptor[0] = srd0.u32All;
amd_queue_->scratch_resource_descriptor[1] = srd1_u32;
amd_queue_->scratch_resource_descriptor[2] = srd2.u32All;
amd_queue_->scratch_resource_descriptor[3] = srd3_u32;
// Populate flat scratch parameters in amd_queue_.
amd_queue_->scratch_backing_memory_location = scratch_base;
amd_queue_->scratch_backing_memory_byte_size = scratch_size_;
// For backwards compatibility this field records the per-lane scratch
// for a 64 lane wavefront. If scratch was allocated for 32 lane waves
// then the effective size for a 64 lane wave is halved.
amd_queue_->scratch_wave64_lane_byte_size = scratch_size_per_wave_ / 64;
if (device->Major() < 11) {
COMPUTE_TMPRING_SIZE tmpring_size;
tmpring_size.bits.WAVESIZE = scratch_size_per_wave_ / 1024;
tmpring_size.bits.WAVES = scratch_waves_;
amd_queue_->compute_tmpring_size = tmpring_size.u32All;
} else if (device->Major() == 11) {
COMPUTE_TMPRING_SIZE_GFX11 tmpring_size;
tmpring_size.bits.WAVESIZE = scratch_size_per_wave_ >> 8;
tmpring_size.bits.WAVES = scratch_waves_ / device->NumShaderEngine();
amd_queue_->compute_tmpring_size = tmpring_size.u32All;
} else {
COMPUTE_TMPRING_SIZE_GFX12 tmpring_size = {};
tmpring_size.bits.WAVESIZE = scratch_size_per_wave_ >> 8;
tmpring_size.bits.WAVES = scratch_waves_ / device->NumShaderEngine();
amd_queue_->compute_tmpring_size = tmpring_size.u32All;
}
return;
}
bool ComputeQueue::UpdateScratch(uint32_t private_segment_size, bool wave32) {
const uint32_t wavefront = wave32 ? 32 : 64;
const uint32_t alignment = 1024 / wavefront;
private_segment_size = AlignUp(private_segment_size, alignment);
uint32_t scratch_size_per_wave = private_segment_size * wavefront;
uint32_t scratch_size = scratch_size_per_wave * scratch_waves_;
if (scratch_size_ >= scratch_size)
return true;
debug_print("need realloc scratch buffer, size %x -> %x\n",
scratch_size_, scratch_size);
GpuMemoryCreateInfo create_info{};
create_info.size = scratch_size;
create_info.domain = rocr_proxy::kLocal;
GpuMemory *gpu_mem = nullptr;
auto code = device->CreateGpuMemory(create_info, &gpu_mem);
if (code != ErrorCode::Success)
return false;
if (scratch_base_) {
auto scratch_gpu_mem = GpuMemory::Convert(scratch_mem_);
delete scratch_gpu_mem;
}
scratch_size_per_wave_ = scratch_size_per_wave;
scratch_size_ = scratch_size;
scratch_base_ = reinterpret_cast<void *>(gpu_mem->GpuAddress());
scratch_mem_ = gpu_mem->GetGpuMemoryHandle();
InitScratchSRD();
return true;
}
bool ComputeQueue::RelocateCmdbufScratchBase(uint64_t addr) {
if (scratch_base_offset_array_.empty())
return true;
for (size_t i = 0; i < scratch_base_offset_array_.size(); i++) {
uint32_t *p_compute_user_data =
reinterpret_cast<uint32_t *>(addr + scratch_base_offset_array_[i]);
if (device->Major() >= 11) {
p_compute_user_data[0] = Ptr48Low32(scratch_base_);
p_compute_user_data[1] = Ptr48High8(scratch_base_);
} else {
p_compute_user_data[0] = PtrLow32(scratch_base_);
p_compute_user_data[1] = (p_compute_user_data[1] & 0xffff0000) | PtrHigh32(scratch_base_);
}
}
scratch_base_offset_array_.clear();
return true;
}
uint32_t ComputeQueue::UpdateIndexStride(uint32_t srd, bool wave32) {
assert(device->Major() < 13);
if (device->Major() == 10) {
SQ_BUF_RSRC_WORD3_GFX10 srd3;
srd3.u32All = srd;
srd3.bits.INDEX_STRIDE = wave32 ? 2 : 3;
return srd3.u32All;
} else if (device->Major() == 11) {
SQ_BUF_RSRC_WORD3_GFX11 srd3;
srd3.u32All = srd;
srd3.bits.INDEX_STRIDE = wave32 ? 2 : 3;
return srd3.u32All;
} else if (device->Major() == 12) {
SQ_BUF_RSRC_WORD3_GFX12 srd3;
srd3.u32All = srd;
srd3.bits.INDEX_STRIDE = wave32 ? 2 : 3;
return srd3.u32All;
}
return srd;
}
extern "C" hsa_status_t (*fn_hsa_ven_amd_loader_query_host_address)(
const void *device_address,
const void **host_address);
uint64_t ComputeQueue::GetKernelObjAddr(uint64_t addr) const {
//TODO: convert dev_addr to host_addr
uint64_t host_addr = 0;
auto ret = fn_hsa_ven_amd_loader_query_host_address(reinterpret_cast<const void *>(addr),
reinterpret_cast<const void **>(&host_addr));
if (ret == HSA_STATUS_ERROR_INVALID_ARGUMENT) {
return NULL;
}
return host_addr;
}
void ComputeQueue::RingDoorbell() {
thread_cond_lock_.lock();
thread_cond_lock_.unlock();
debug_print("notify %p wptr=%" PRIx64 " rptr=%" PRIx64 "\n",
ring, GetRingWptr()->load(), GetRingRptr()->load());
thread_cond_.notify_one();
}
hsa_status_t ComputeQueue::Init(void) {
hsa_status_t ret = use_hws ? HwsInit() : SwsInit();
if (ret)
return ret;
ib_start_addr = cmdbuf_addr;
cmdbuf_aql_frame_size = device->GetAqlFrameSize();
return ret;
}
hsa_status_t ComputeQueue::Fini(void) {
return use_hws ? HwsFini() : SwsFini();
}
hsa_status_t ComputeQueue::PreSubmit(void) {
if (!device->WaitPagingFence(this))
return HSA_STATUS_ERROR;
RelocateCmdbufScratchBase(ib_start_addr);
return HSA_STATUS_SUCCESS;
}
hsa_status_t ComputeQueue::EndSubmit(void) {
// record last submitted cmdbuf_aql_frame_write_index to see if GPU is hungry
sync_point = cmdbuf_aql_frame_write_index;
ib_start_addr = cmdbuf_addr +
(cmdbuf_aql_frame_write_index % WDDMDevice::GetAqlFrameNum()) *
cmdbuf_aql_frame_size;
ib_size = 0;
return HSA_STATUS_SUCCESS;
}
hsa_status_t ComputeQueue::Submit(void) {
hsa_status_t ret = PreSubmit();
if (ret)
return HSA_STATUS_ERROR;
ret = use_hws ?
HwsSubmit(ib_start_addr, ib_size, cmdbuf_aql_frame_write_index) :
SwsSubmit(ib_start_addr, ib_size, cmdbuf_aql_frame_write_index);
if (ret)
return HSA_STATUS_ERROR;
ret = EndSubmit();
if (ret)
return HSA_STATUS_ERROR;
return HSA_STATUS_SUCCESS;
}
hsa_status_t
ComputeQueue::KernelDispatchAqlToPm4(char *cpu, hsa_kernel_dispatch_packet_t *packet) {
debug_print("queue %p kernel dispatch head=%x setup=%x wx=%x wy=%x wz=%x "
"gx=%x gy=%x gz=%x ps=%x gs=%x ko=%" PRIx64 " ka=%p cs=%" PRIx64 "\n",
ring, packet->header,
packet->setup, packet->workgroup_size_x, packet->workgroup_size_y,
packet->workgroup_size_z, packet->grid_size_x, packet->grid_size_y,
packet->grid_size_z, packet->private_segment_size,
packet->group_segment_size, packet->kernel_object, packet->kernarg_address,
packet->completion_signal.handle);
if (packet->workgroup_size_x > 1024 ||
packet->workgroup_size_y > 1024 ||
packet->workgroup_size_z > 1024)
return HSA_STATUS_ERROR_INVALID_ARGUMENT;
int major = device->Major();
int i = ib_size;
const amd_kernel_code_t* kernel_object =
(const amd_kernel_code_t *)GetKernelObjAddr(packet->kernel_object);
if (kernel_object == NULL) {
return HSA_STATUS_ERROR_INVALID_CODE_OBJECT;
}
void* entry = (void*)(packet->kernel_object + kernel_object->kernel_code_entry_byte_offset);
assert((size_t)entry % AMD_ISA_ALIGN_BYTES == 0);
debug_print("kernel object property=%x entry=%p lds=%x+%x\n",
kernel_object->kernel_code_properties, entry,
kernel_object->workgroup_group_segment_byte_size,
packet->group_segment_size);
if (packet->setup == 0 || packet->setup > 3)
return HSA_STATUS_ERROR_INCOMPATIBLE_ARGUMENTS;
if (packet->group_segment_size > device->LdsSize())
return HSA_STATUS_ERROR_INVALID_ALLOCATION;
uint32_t lds_blks = device->LdsBlocks(packet);
if (lds_blks > 128)
return HSA_STATUS_ERROR_INVALID_ARGUMENT;
const bool wave32 =
AMD_HSA_BITS_GET(kernel_object->kernel_code_properties,
AMD_KERNEL_CODE_PROPERTIES_ENABLE_WAVEFRONT_SIZE32);
assert(packet->private_segment_size >= kernel_object->workitem_private_segment_byte_size);
UpdateScratch(packet->private_segment_size, wave32);
amd_signal_t *signal = (amd_signal_t *)packet->completion_signal.handle;
// Record start timestamp when enabling profiling
if (signal && EnableProfiling())
i += cmd_util.BuildCopyData(&signal->start_ts, cpu + i);
// Build a barrier packet if it is requested
const bool is_barrier_packet = (packet->header >> HSA_PACKET_HEADER_BARRIER) & 0x1;
if (is_barrier_packet && needs_barrier)
i += cmd_util.BuildBarrier(cpu + i);
// flush cache
i += cmd_util.BuildAcquireMem(major, cpu + i);
if (major >= 11) {
AppendCmdbufSratchBaseOffset(
i + offsetof(struct SetScratchTemplate, scratch_lo));
i += cmd_util.BuildScratch(ScratchBase(), cpu + i);
i += cmd_util.BuildComputeShaderParams(cpu + i);
}
struct DispatchInfo info;
info.major = major;
info.pPacket = packet;
info.pEntry = entry;
info.pKernelObject = kernel_object;
info.ldsBlks = lds_blks;
info.pAmdQueue = amd_queue_;
info.wave32 = wave32;
info.srd = UpdateIndexStride(
info.pAmdQueue->scratch_resource_descriptor[3], wave32);
info.pScratchBase = ScratchBase();
info.scratchSizePerWave = ScratchSizePerWave();
memset(info.scratchBaseOffset, 0, sizeof(info.scratchBaseOffset));
info.offsetCnt = 0;
size_t size;
size = cmd_util.BuildDispatch(&info, cpu + i);
for (int j = 0; j < info.offsetCnt; j++)
AppendCmdbufSratchBaseOffset(i + info.scratchBaseOffset[j]);
i += size;
needs_barrier = (packet->completion_signal.handle == 0);
if (signal) {
// wait cs done
i += cmd_util.BuildBarrier(cpu + i);
// Record end timestamp when enabling profiling
if (EnableProfiling())
i += cmd_util.BuildCopyData(&signal->end_ts, cpu + i);
// flush cache
i += cmd_util.BuildAcquireMem(major, cpu + i);
assert(signal->kind == AMD_SIGNAL_KIND_USER);
uint64_t *signal_addr = (uint64_t *)&signal->value;
debug_print("signal value=%" PRIx64 "\n", signal->value);
i += cmd_util.BuildAtomicMem(signal_addr, TC_OP_ATOMIC_ADD_RTN_64, cpu + i, cache_policy__mec_atomic_mem__bypass, -1);
}
// The ring_rptr is used to record pm4 queue rptr value,
// dispatch readptr position, this is used to share rptr with
// aql queue.
i += cmd_util.BuildAtomicMem((uint64_t *)ring_rptr, TC_OP_ATOMIC_ADD_RTN_64, cpu + i);
ib_size = i;
cmdbuf_aql_frame_write_index++;
packet->header = HSA_PACKET_TYPE_INVALID;
return HSA_STATUS_SUCCESS;
}
extern "C" hsa_signal_value_t (*fn_hsa_signal_load_relaxed)(
hsa_signal_t signal);
extern "C" hsa_signal_value_t (*fn_hsa_signal_wait_relaxed)(
hsa_signal_t signal,
hsa_signal_condition_t condition,
hsa_signal_value_t compare_value,
uint64_t timeout_hint,
hsa_wait_state_t wait_state_hint);
hsa_status_t
ComputeQueue::BarrierGenericAqlToPm4(char *cpu, hsa_barrier_and_packet_t *packet, bool is_or) {
debug_print("queue %p %s head=%x dep %" PRIx64 " %" PRIx64 " %" PRIx64
" %" PRIx64 " %" PRIx64 " cs=%" PRIx64"\n",
ring, is_or ? "or" : "and",
packet->header, packet->dep_signal[0].handle,
packet->dep_signal[1].handle, packet->dep_signal[2].handle,
packet->dep_signal[3].handle, packet->dep_signal[4].handle,
packet->completion_signal.handle);
// fix me: can we use gpu packet?
if (is_or) {
bool unsignaled = true;
hsa_signal_t sig[5];
int n = 0;
for (int i = 0; i < 5; i++) {
if (packet->dep_signal[i].handle)
sig[n++] = packet->dep_signal[i];
}
while (n) {
for (int i = 0; i < n; i++) {
if (!fn_hsa_signal_load_relaxed(sig[i])) {
unsignaled = false;
break;
}
}
if (!unsignaled)
break;
std::this_thread::sleep_for(std::chrono::microseconds(20));
}
} else {
for (int i = 0; i < 5; i++) {
if (!packet->dep_signal[i].handle)
continue;
hsa_signal_value_t value =
fn_hsa_signal_wait_relaxed(packet->dep_signal[i], HSA_SIGNAL_CONDITION_EQ, 0, UINT64_MAX, HSA_WAIT_STATE_BLOCKED);
assert(value == 0);
}
}
int major = device->Major();
int i = ib_size;
if (packet->completion_signal.handle != 0) {
amd_signal_t *signal = (amd_signal_t *)packet->completion_signal.handle;
assert(signal->kind == AMD_SIGNAL_KIND_USER);
uint64_t *signal_addr = (uint64_t *)&signal->value;
debug_print("signal value=%" PRIx64 "\n", signal->value);
// Record start timestamp when enabling profiling
if (EnableProfiling())
i += cmd_util.BuildCopyData(&signal->start_ts, cpu + i);
if (needs_barrier)
i += cmd_util.BuildBarrier(cpu + i);
needs_barrier = false;
// Record end timestamp when enabling profiling
if (EnableProfiling())
i += cmd_util.BuildCopyData(&signal->end_ts, cpu + i);
// flush cache
i += cmd_util.BuildAcquireMem(major, cpu + i);
i += cmd_util.BuildAtomicMem(signal_addr, TC_OP_ATOMIC_ADD_RTN_64, cpu + i, cache_policy__mec_atomic_mem__bypass, -1);
}
// The ring_rptr is used to record pm4 queue rptr value,
// dispatch readptr position, this is used to share rptr with
// aql queue.
i += cmd_util.BuildAtomicMem((uint64_t *)ring_rptr, TC_OP_ATOMIC_ADD_RTN_64, cpu + i);
ib_size = i;
cmdbuf_aql_frame_write_index++;
packet->header = HSA_PACKET_TYPE_INVALID;
return HSA_STATUS_SUCCESS;
}
hsa_status_t ComputeQueue::VendorSpecificAqlToPm4(char *cpu, amd_aql_pm4_ib *packet) {
constexpr uint32_t AMD_AQL_FORMAT_PM4_IB = 0x1;
assert(packet->ven_hdr == AMD_AQL_FORMAT_PM4_IB);
uint8_t op = (packet->ib_jump_cmd[0] >> PM4_OPCODE_SHIFT) & 0xff;
assert(op == IT_INDIRECT_BUFFER);
uint32_t* pm4_addr = reinterpret_cast<uint32_t*>((static_cast<uint64_t>(packet->ib_jump_cmd[2]) << 32) | (static_cast<uint64_t>(packet->ib_jump_cmd[1]) & ~3ull));
uint32_t pm4_size = packet->ib_jump_cmd[3]&0xfffff;
debug_print("queue %p %s VENDOR_SPECIFIC pkt pm4_addr %p pm4_size %" PRIx64 " cs=%" PRIx64"\n",
ring, vendor_packet_support ? "process" : "skip", pm4_addr, pm4_size,
packet->completion_signal.handle);
for (int i = 0; i < pm4_size; i++) {
debug_print("pm4_addr[%d]=%#x\n", i, pm4_addr[i]);
}
if (vendor_packet_support) {
int major = device->Major();
int i = ib_size;
memcpy(cpu+i, pm4_addr, pm4_size * sizeof(uint32_t));
i += pm4_size * sizeof(uint32_t);
if (packet->completion_signal.handle != 0) {
amd_signal_t *signal = (amd_signal_t *)packet->completion_signal.handle;
assert(signal->kind == AMD_SIGNAL_KIND_USER);
uint64_t *signal_addr = (uint64_t *)&signal->value;
debug_print("signal value=%" PRIx64 "\n", signal->value);
// Record start timestamp when enabling profiling
if (EnableProfiling())
i += cmd_util.BuildCopyData(&signal->start_ts, cpu + i);
//if (needs_barrier)
i += cmd_util.BuildBarrier(cpu + i);
//needs_barrier = false;
// Record end timestamp when enabling profiling
if (EnableProfiling())
i += cmd_util.BuildCopyData(&signal->end_ts, cpu + i);
// flush cache
i += cmd_util.BuildAcquireMem(major, cpu + i);
i += cmd_util.BuildAtomicMem(signal_addr, TC_OP_ATOMIC_ADD_RTN_64, cpu + i, cache_policy__mec_atomic_mem__bypass, -1);
}
// The ring_rptr is used to record pm4 queue rptr value,
// dispatch readptr position, this is used to share rptr with
// aql queue.
i += cmd_util.BuildAtomicMem((uint64_t *)ring_rptr, TC_OP_ATOMIC_ADD_RTN_64, cpu + i);
ib_size = i;
} else {
if (packet->completion_signal.handle != 0) {
fn_hsa_signal_store_screlease(packet->completion_signal, 0);
}
}
cmdbuf_aql_frame_write_index++;
packet->header = HSA_PACKET_TYPE_INVALID;
return HSA_STATUS_SUCCESS;
}
hsa_status_t ComputeQueue::SwitchAql2PM4(void) {
uint16_t *packet = (uint16_t *) ((char *)ring +
(cmdbuf_aql_frame_write_index % ring_size) * 64);
uint16_t header = (*packet >> HSA_PACKET_HEADER_TYPE);
header &= (1 << HSA_PACKET_HEADER_WIDTH_TYPE) - 1;
hsa_kernel_dispatch_packet_t *aql_packet =
(hsa_kernel_dispatch_packet_t *)packet;
hsa_status_t ret;
switch (header) {
case HSA_PACKET_TYPE_KERNEL_DISPATCH:
ret = KernelDispatchAqlToPm4((char *)ib_start_addr, aql_packet);
if (ret != HSA_STATUS_SUCCESS)
return ret;
// Stop merging packages util below conditions are met:
// 1) The kernel with completion signal;
// 2) The cmdbuf_aql_frame_write_index reaches the end of cmdbuf
// 3) The queue is empty now, submit the package right now.
if (!(aql_packet->completion_signal.handle) &&
(cmdbuf_aql_frame_write_index % WDDMDevice::GetAqlFrameNum()) &&
(*sync_addr != sync_point))
return HSA_STATUS_SUCCESS;
break;
case HSA_PACKET_TYPE_BARRIER_AND:
BarrierGenericAqlToPm4((char *)ib_start_addr, (hsa_barrier_and_packet_t *)aql_packet);
break;
case HSA_PACKET_TYPE_BARRIER_OR:
BarrierGenericAqlToPm4((char *)ib_start_addr, (hsa_barrier_and_packet_t *)aql_packet, true);
break;
case HSA_PACKET_TYPE_VENDOR_SPECIFIC:
VendorSpecificAqlToPm4((char *)ib_start_addr, (amd_aql_pm4_ib *)aql_packet);
break;
case HSA_PACKET_TYPE_INVALID:
// When packets are submitted out of order, the format field of current AQL packet
// may not have been updated yet and is still INVALID. Return HSA_STATUS_SUCCESS and
// do not process AQL packets before the packet format field is updated.
assert(false && "Should not reach here, HSA_PACKET_TYPE_INVALID has been filtered in upper layer");
return HSA_STATUS_SUCCESS;
default:
return HSA_STATUS_ERROR_INVALID_PACKET_FORMAT;
}
ready_to_submit = true;
return HSA_STATUS_SUCCESS;
}
hsa_status_t ComputeQueue::Process(void) {
while (cmdbuf_aql_frame_write_index < ring_wptr->load() &&
!IsInvalidPacket()) {
debug_print("process %p wptr=%" PRIx64 " rptr=%" PRIx64 "\n",
ring, ring_wptr->load(), ring_rptr->load());
hsa_status_t ret;
// wait for next few cmdbuf slots to be free
// If wptr catch up the rptr in the cmdbuf, this needs wait for the rptr to free the cmdbuf.
// Here the wptr comes from queue->cmdbuf_aql_frame_write_index, while rptr comes from *queue->sync_addr.
if (*sync_addr + WDDMDevice::GetAqlFrameNum() <= cmdbuf_aql_frame_write_index) {
uint64_t value = cmdbuf_aql_frame_write_index - WDDMDevice::GetAqlFrameNum() + 1;
if (!device->CpuWait(&syncobj, &value, 1, false))
return HSA_STATUS_ERROR;
}
ret = SwitchAql2PM4();
if (ret != HSA_STATUS_SUCCESS)
return ret;
if (!ready_to_submit)
continue;
ret = Submit();
if (ret != HSA_STATUS_SUCCESS)
return ret;
ready_to_submit = false;
debug_print("done %p wptr=%" PRIx64 " rptr=%" PRIx64 "\n",
ring, ring_wptr->load(), ring_rptr->load());
}
return HSA_STATUS_SUCCESS;
}
hsa_status_t SDMAQueue::Init(void) {
hsa_status_t ret = use_hws ? HwsInit() : SwsInit();
if (ret)
return ret;
std::memset((char *)cmdbuf_addr, 0, cmdbuf_size);
return ret;
}
hsa_status_t SDMAQueue::Fini(void) {
return use_hws ? HwsFini() : SwsFini();
}
int SDMAQueue::PreparePacket(uint32_t offset, uint64_t size) {
ib_start_addr = cmdbuf_addr + offset;
ib_size = size;
rptr_next += ib_size;
return STATUS_SUCCESS;
}
hsa_status_t SDMAQueue::Submit(void) {
if (!device->WaitPagingFence(this))
return HSA_STATUS_ERROR;
int ret = use_hws ?
HwsSubmit(ib_start_addr, ib_size, rptr_next) :
SwsSubmit(ib_start_addr, ib_size, rptr_next);
if (ret)
return HSA_STATUS_ERROR;
return HSA_STATUS_SUCCESS;
}
}
}
+163
Parādīt failu
@@ -0,0 +1,163 @@
#include <cassert>
#include <map>
#include <algorithm>
#include "inc/wddm/va_mgr.h"
using namespace std;
namespace rocr {
namespace core {
VaMgr::VaMgr(uint64_t start, uint64_t size, uint64_t min_align) {
min_align_ = min_align;
auto free_it = free_list_.insert(make_pair(size, start));
frag_map_[start] = make_fragment(free_it, size);
}
VaMgr::~VaMgr() {
assert(free_list_.size() == 1);
assert(frag_map_.size() == 1);
free_list_.clear();
frag_map_.clear();
}
uint64_t VaMgr::Alloc(uint64_t bytes, uint64_t align, uint64_t addr) {
if (addr > 0 &&
(align == 0 || (addr % align) == 0)) {
lock_guard<mutex> gard(lock_);
auto frag_it = frag_map_.upper_bound(addr);
assert(frag_it != frag_map_.begin());
--frag_it;
while (frag_it != frag_map_.begin()) {
const uint64_t base = frag_it->first;
const uint64_t size = frag_it->second.size;
// Cannot find free fragment contains the target `addr`
if (bytes > size || addr < base || addr + bytes >= base + size ||
!is_free(frag_it->second)) {
--frag_it;
continue;
} else if (addr >= base + size)
break;
// Try to allocate target `addr` from this free fragment
auto free_it = frag_it->second.free_list_entry_;
assert(free_it != free_list_.end());
free_list_.erase(free_it);
frag_it->second.size = bytes;
set_used(frag_it->second);
// [base, addr)
if (addr > base) add_free_fragment(addr - base, base);
// [addr, addr + bytes) is used
// [addr + bytes, base + size)
if (base + size > addr + bytes) add_free_fragment(base + size - addr - bytes, addr + bytes);
return addr;
}
}
// Allocate not fixed address
return AllocImpl(bytes, align);
}
uint64_t VaMgr::AllocImpl(const uint64_t bytes, const uint64_t align) {
uint64_t addr = 0;
uint64_t align_bytes = bytes;
const int retry = align == 0 ? 0 : 1;
const uint64_t new_align = align == 0 ? min_align_ : AlignUp(align, min_align_);
lock_guard<mutex> gard(lock_);
for (int i = 0; i <= retry; i++) {
auto free_it = free_list_.lower_bound(align_bytes);
if (free_it == free_list_.end()) break;
uint64_t base = free_it->second;
uint64_t size = free_it->first;
assert(size >= align_bytes);
auto fragment = frag_map_.find(base);
assert(fragment != frag_map_.end());
assert(size == fragment->second.size);
uint64_t delta = align == 0 ? 0 : base % align;
if (delta == 0) {
// already find aligned address
addr = base;
free_list_.erase(free_it);
fragment->second.size = bytes;
set_used(fragment->second);
if (size > bytes) add_free_fragment(size - bytes, base + bytes);
break;
} else if (i == 0) {
align_bytes += new_align;
continue;
} else {
uint64_t aligned_base = base + align - delta;
addr = aligned_base;
free_list_.erase(free_it);
add_used_fragment(bytes, aligned_base);
add_free_fragment(aligned_base - base, base);
if (size > aligned_base - base + bytes)
add_free_fragment(size - (aligned_base - base) - bytes, aligned_base + bytes);
break;
}
}
return addr;
}
void VaMgr::Free(uint64_t addr) {
if (addr == 0) return;
lock_guard<mutex> gard(lock_);
auto frag_it = frag_map_.find(addr);
if (frag_it == frag_map_.end() || is_free(frag_it->second)) return;
uint64_t base = addr;
// Merge lower
if (frag_it != frag_map_.begin()) {
auto lower = frag_it;
--lower;
if (is_free(lower->second)) {
remove_free_list_entry(lower->second);
base -= lower->second.size;
lower->second.size += frag_it->second.size;
frag_map_.erase(frag_it);
frag_it = lower;
}
}
// Merge upper
{
auto upper = frag_it;
++upper;
if (upper != frag_map_.end() && is_free(upper->second)) {
remove_free_list_entry(upper->second);
frag_it->second.size += upper->second.size;
frag_map_.erase(upper);
}
}
uint64_t size = frag_it->second.size;
auto it = free_list_.insert(make_pair(size, base));
set_free(frag_it->second, it);
}
} // namespace core
} // namespace rocr