Files
TheRock/rocm-systems/projects/hip-tests/catch/unit/virtualMemoryManagement/hipMemMap.cc
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1191 lines
42 KiB
C++

/*
* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
*
* SPDX-License-Identifier: MIT
*/
/**
* @addtogroup hipMemMap hipMemMap
* @{
* @ingroup VirtualMemoryManagementTest
* `hipError_t hipMemMap (void* ptr,
* size_t size,
* size_t offset,
* hipMemGenericAllocationHandle_t handle,
* unsigned long long flags)` -
* Maps an allocation handle to a reserved virtual address range.
*/
#include <hip_test_common.hh>
#include <atomic>
#include <chrono>
#include <thread>
#include "hip_vmm_common.hh"
constexpr int N = (1 << 13);
constexpr int num_buf = 3;
constexpr int initializer = 0;
/**
Kernel to perform Square of input data.
*/
static __global__ void square_kernel(int* Buff) {
int i = threadIdx.x + blockDim.x * blockIdx.x;
int temp = Buff[i] * Buff[i];
Buff[i] = temp;
}
/**
* Test Description
* ------------------------
* - Check if a physical chunk can be mapped/unmapped to same
* vmm address range repeatedly. This test validates physical memory
* euse using same vmm range.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_SameMemoryReuse) {
constexpr int iterations = 20;
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
CTX_CREATE();
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
SECTION("Memory Allocation Type as hipMemAllocationTypePinned") {
prop.type = hipMemAllocationTypePinned;
}
#if HT_AMD
SECTION("Memory Allocation Type as hipMemAllocationTypeUncached") {
prop.type = hipMemAllocationTypeUncached;
}
#endif
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device; // Current Devices
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
// Allocate host memory and intialize data
std::vector<int> A_h(N), B_h(N), C_h(N);
// Initialize with data
for (size_t idx = 0; idx < N; idx++) {
A_h[idx] = idx;
C_h[idx] = idx * idx;
}
// Allocate a physical memory chunk
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
// Allocate num_buf virtual address ranges
void* ptrA;
HIP_CHECK(hipMemAddressReserve(&ptrA, size_mem, 0, 0, 0));
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
for (int i = 0; i < iterations; i++) {
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemMap(ptrA, size_mem, 0, handle, 0));
// Set access to GPU 0
HIP_CHECK(hipMemSetAccess(ptrA, size_mem, &accessDesc, 1));
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA), A_h.data(), buffer_size));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA), buffer_size));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
square_kernel<<<dim3(N / threadsPerBlk), dim3(threadsPerBlk), 0, 0>>>(
reinterpret_cast<int*>(ptrA));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA), buffer_size));
HIP_CHECK(hipStreamSynchronize(0));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), C_h.data()));
HIP_CHECK(hipMemUnmap(ptrA, size_mem));
}
// Release resources
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(ptrA, size_mem));
CTX_DESTROY();
}
/**
* Test Description
* ------------------------
* - Check if a physical chunk can be mapped/unmapped for multiple
* vmm addresses. This test validates physical memory reuse using
* different vmm ranges.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_PhysicalMemoryReuse_SingleGPU) {
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
CTX_CREATE();
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
SECTION("Memory Allocation Type as hipMemAllocationTypePinned") {
prop.type = hipMemAllocationTypePinned;
}
#if HT_AMD
SECTION("Memory Allocation Type as hipMemAllocationTypeUncached") {
prop.type = hipMemAllocationTypeUncached;
}
#endif
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device; // Current Devices
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
// Allocate host memory and intialize data
std::vector<int> A_h(N), B_h(N), C_h(N);
// Initialize with data
for (size_t idx = 0; idx < N; idx++) {
A_h[idx] = idx;
C_h[idx] = idx * idx;
}
// Allocate a physical memory chunk
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
// Allocate num_buf virtual address ranges
void* ptrA[num_buf];
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemAddressReserve(&ptrA[buf], size_mem, 0, 0, 0));
}
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
for (int buf = 0; buf < num_buf; buf++) {
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemMap(ptrA[buf], size_mem, 0, handle, 0));
// Set access to GPU 0
HIP_CHECK(hipMemSetAccess(ptrA[buf], size_mem, &accessDesc, 1));
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA[buf]), A_h.data(), buffer_size));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA[buf]), buffer_size));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
square_kernel<<<dim3(N / threadsPerBlk), dim3(threadsPerBlk), 0, 0>>>(
reinterpret_cast<int*>(ptrA[buf]));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA[buf]), buffer_size));
HIP_CHECK(hipStreamSynchronize(0));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), C_h.data()));
HIP_CHECK(hipMemUnmap(ptrA[buf], size_mem));
}
// Release resources
HIP_CHECK(hipMemRelease(handle));
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemAddressFree(ptrA[buf], size_mem));
}
CTX_DESTROY();
}
/**
* Test Description
* ------------------------
* - Check if a physical chunk can be mapped to multiple
* vmm addresses at the same time and check data values integrity
* between different VMMs.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_PhysicalMemory_Map2MultVMMs) {
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
CTX_CREATE();
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
SECTION("Memory Allocation Type as hipMemAllocationTypePinned") {
prop.type = hipMemAllocationTypePinned;
}
#if HT_AMD
SECTION("Memory Allocation Type as hipMemAllocationTypeUncached") {
prop.type = hipMemAllocationTypeUncached;
}
#endif
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device; // Current Devices
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
// Allocate host memory and intialize data
std::vector<int> A_h(N), B_h(N);
// Initialize with data
for (size_t idx = 0; idx < N; idx++) {
A_h[idx] = idx;
}
// Allocate a physical memory chunk
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
// Allocate num_buf virtual address ranges
void* ptrA[num_buf];
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemAddressReserve(&ptrA[buf], size_mem, 0, 0, 0));
}
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemMap(ptrA[buf], size_mem, 0, handle, 0));
}
// Set access for all the buffers.
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemSetAccess(ptrA[buf], size_mem, &accessDesc, 1));
}
// Copy data to VMM via ptrA[0]
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA[0]), A_h.data(), buffer_size));
// Validate the data contained in VMM using ptrA[0], ptrA[1],
// ......, ptrA[num_buf-1]
for (int buf = 0; buf < num_buf; buf++) {
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA[buf]), buffer_size));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
}
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemUnmap(ptrA[buf], size_mem));
}
// Release resources
HIP_CHECK(hipMemRelease(handle));
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemAddressFree(ptrA[buf], size_mem));
}
CTX_DESTROY();
}
/**
* Test Description
* ------------------------
* - Allocate one physical handle and map it to N (>=2) different VAs.
* Write a known pattern via VA #0, unmap VA #0 only, then read from
* VA #1 to verify the surviving alias is still coherent. Finally, map
* the same handle to a fresh VA #N and read back to prove the handle
* was not released early.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_UnmapOneAliasOfMultiAliasedHandle) {
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
CTX_CREATE();
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device;
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
// Step 1: Allocate one physical handle.
hipMemGenericAllocationHandle_t handle;
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
// Reserve N (>=2) VAs and map the handle to each.
void* ptrA[num_buf];
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemAddressReserve(&ptrA[buf], size_mem, 0, 0, 0));
HIP_CHECK(hipMemMap(ptrA[buf], size_mem, 0, handle, 0));
}
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemSetAccess(ptrA[buf], size_mem, &accessDesc, 1));
}
// Step 2: write known pattern via VA #0.
std::vector<int> A_h(N), B_h(N);
for (size_t idx = 0; idx < N; idx++) {
A_h[idx] = static_cast<int>(idx) + 0xA5A5;
}
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA[0]), A_h.data(), buffer_size));
// Step 3: unmap VA #0 only. Handle and remaining aliases must survive.
HIP_CHECK(hipMemUnmap(ptrA[0], size_mem));
// Step 4: read from VA #1. Pattern must be visible.
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA[1]), buffer_size));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
// Step 5: handle must NOT be released early. Map it to a fresh VA #N and
// read back. If the handle had been incorrectly released the map would
// fail or the data would be garbage.
void* ptrFresh = nullptr;
HIP_CHECK(hipMemAddressReserve(&ptrFresh, size_mem, 0, 0, 0));
HIP_CHECK(hipMemMap(ptrFresh, size_mem, 0, handle, 0));
HIP_CHECK(hipMemSetAccess(ptrFresh, size_mem, &accessDesc, 1));
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrFresh), buffer_size));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
// Cleanup: unmap surviving VAs (skip ptrA[0], already unmapped).
HIP_CHECK(hipMemUnmap(ptrFresh, size_mem));
for (int buf = 1; buf < num_buf; buf++) {
HIP_CHECK(hipMemUnmap(ptrA[buf], size_mem));
}
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(ptrFresh, size_mem));
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemAddressFree(ptrA[buf], size_mem));
}
CTX_DESTROY();
}
void physicalMemoryReuse_MultiDev (hipMemAllocationProp prop) {
int devicecount = 0;
HIP_CHECK(hipGetDeviceCount(&devicecount));
if (devicecount < 2) {
HIP_SKIP_TEST(HipTest::SkipReason::kFewerThanTwoGpus);
}
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
for (int devX = 0; devX < devicecount; devX++) {
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, devX));
checkVMMSupported(device);
prop.location.id = device; // Current Devices
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
// Allocate host memory and intialize data
std::vector<int> A_h(N), B_h(N);
// Initialize with data
for (size_t idx = 0; idx < N; idx++) {
A_h[idx] = idx;
}
// Allocate a physical memory chunk
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
// Allocate devicecount virtual address ranges
std::vector<void*> ptrA(devicecount);
for (int devY = 0; devY < devicecount; devY++) {
HIP_CHECK(hipMemAddressReserve(&ptrA[devY], size_mem, 0, 0, 0));
}
for (int devY = 0; devY < devicecount; devY++) {
hipDevice_t deviceToTest;
HIP_CHECK(hipDeviceGet(&deviceToTest, devY));
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = deviceToTest;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
HIP_CHECK(hipSetDevice(devY));
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemMap(ptrA[devY], size_mem, 0, handle, 0));
// Set access to GPU 0
HIP_CHECK(hipMemSetAccess(ptrA[devY], size_mem, &accessDesc, 1));
HIP_CHECK(
hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA[devY]), A_h.data(), buffer_size));
HIP_CHECK(
hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA[devY]), buffer_size));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
HIP_CHECK(hipMemUnmap(ptrA[devY], size_mem));
}
HIP_CHECK(hipSetDevice(0)); // set the device back to 0.
// Release resources
HIP_CHECK(hipMemRelease(handle));
for (int devY = 0; devY < devicecount; devY++) {
HIP_CHECK(hipMemAddressFree(ptrA[devY], size_mem));
}
}
}
/**
* Test Description
* ------------------------
* - Check if a physical chunk can be mapped/unmapped for
* multiple vmm addresses. This test validates physical memory
* reuse using different vmm ranges on multiple devices.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_PhysicalMemoryReuse_MultiDev) {
CHECK_P2P_SUPPORT
SECTION("Memory Allocation Type as hipMemAllocationTypePinned") {
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
physicalMemoryReuse_MultiDev(prop);
}
#if HT_AMD
SECTION("Memory Allocation Type as hipMemAllocationTypeUncached") {
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypeUncached;
prop.location.type = hipMemLocationTypeDevice;
physicalMemoryReuse_MultiDev(prop);
}
#endif
}
/**
* Test Description
* ------------------------
* - Check if different physical chunk can be mapped/unmapped
* for single vmm address. This test validates VMM memory reuse
* using different physical ranges.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_VMMMemoryReuse_SingleGPU) {
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
CTX_CREATE();
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
SECTION("Memory Allocation Type as hipMemAllocationTypePinned") {
prop.type = hipMemAllocationTypePinned;
}
#if HT_AMD
SECTION("Memory Allocation Type as hipMemAllocationTypeUncached") {
prop.type = hipMemAllocationTypeUncached;
}
#endif
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device; // Current Devices
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle[num_buf];
// Allocate host memory and intialize data
std::vector<int> A_h(N), B_h(N), C_h(N);
// Initialize with data
for (size_t idx = 0; idx < N; idx++) {
A_h[idx] = idx;
C_h[idx] = idx * idx;
}
// Allocate a physical memory chunk
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemCreate(&handle[buf], size_mem, &prop, 0));
}
// Allocate num_buf virtual address ranges
void* ptrA;
HIP_CHECK(hipMemAddressReserve(&ptrA, size_mem, 0, 0, 0));
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
// Map ptrA to physical chunk
for (int buf = 0; buf < num_buf; buf++) {
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemMap(ptrA, size_mem, 0, handle[buf], 0));
// Set access to GPU 0
HIP_CHECK(hipMemSetAccess(ptrA, size_mem, &accessDesc, 1));
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA), A_h.data(), buffer_size));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA), buffer_size));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
#if HT_NVIDIA
square_kernel<<<dim3(N / threadsPerBlk), dim3(threadsPerBlk), 0, 0>>>(
reinterpret_cast<int*>(ptrA));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA), buffer_size));
HIP_CHECK(hipStreamSynchronize(0));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), C_h.data()));
#endif
HIP_CHECK(hipMemUnmap(ptrA, size_mem));
}
// Release resources
for (int buf = 0; buf < num_buf; buf++) {
HIP_CHECK(hipMemRelease(handle[buf]));
}
HIP_CHECK(hipMemAddressFree(ptrA, size_mem));
CTX_DESTROY();
}
void vMMMemoryReuse_MultiGPU (hipMemAllocationProp prop) {
int deviceId = 0, devicecount = 0;
HIP_CHECK(hipGetDeviceCount(&devicecount));
if (devicecount < 2) {
HIP_SKIP_TEST(HipTest::SkipReason::kFewerThanTwoGpus);
}
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
hipDevice_t device;
HIP_CHECK(hipSetDevice(0));
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
prop.location.id = device; // Current Devices
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
std::vector<hipMemGenericAllocationHandle_t> handle(devicecount);
// Allocate host memory and intialize data
std::vector<int> A_h(N), B_h(N);
// Initialize with data
for (size_t idx = 0; idx < N; idx++) {
A_h[idx] = idx;
}
// Allocate a physical memory chunk
for (int dev = 0; dev < devicecount; dev++) {
hipDevice_t dev_handle;
HIP_CHECK(hipDeviceGet(&dev_handle, dev));
prop.location.id = dev_handle;
HIP_CHECK(hipMemCreate(&handle[dev], size_mem, &prop, 0));
}
// Allocate devicecount virtual address ranges
void* ptrA;
HIP_CHECK(hipMemAddressReserve(&ptrA, size_mem, 0, 0, 0));
// Map ptrA to physical chunk
SECTION("Set Access of VMM to Different GPU") {
for (int dev = 0; dev < devicecount; dev++) {
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, dev));
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
HIP_CHECK(hipSetDevice(dev));
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemMap(ptrA, size_mem, 0, handle[dev], 0));
HIP_CHECK(hipMemSetAccess(ptrA, size_mem, &accessDesc, 1));
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA), A_h.data(), buffer_size));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA), buffer_size));
HIP_CHECK(hipMemUnmap(ptrA, size_mem));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
}
}
SECTION("Set Access of VMM to default GPU") {
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
for (int dev = 0; dev < devicecount; dev++) {
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemMap(ptrA, size_mem, 0, handle[dev], 0));
HIP_CHECK(hipMemSetAccess(ptrA, size_mem, &accessDesc, 1));
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA), A_h.data(), buffer_size));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA), buffer_size));
HIP_CHECK(hipMemUnmap(ptrA, size_mem));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
}
}
HIP_CHECK(hipSetDevice(0));
// Release resources
for (int dev = 0; dev < devicecount; dev++) {
HIP_CHECK(hipMemRelease(handle[dev]));
}
HIP_CHECK(hipMemAddressFree(ptrA, size_mem));
}
/**
* Test Description
* ------------------------
* - Check if different physical chunk allocated in different devices
* can be mapped/unmapped to single vmm address. This test validates VMM
* memory reuse using different physical ranges.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_VMMMemoryReuse_MultiGPU) {
CHECK_P2P_SUPPORT
SECTION("Memory Allocation Type as hipMemAllocationTypePinned") {
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
vMMMemoryReuse_MultiGPU(prop);
}
#if HT_AMD
SECTION("Memory Allocation Type as hipMemAllocationTypeUncached") {
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypeUncached;
prop.location.type = hipMemLocationTypeDevice;
vMMMemoryReuse_MultiGPU(prop);
}
#endif
}
/**
* Test Description
* ------------------------
* - Check if a partial part of a VMM range can be mapped/unmapped
* to a physical address.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_MapPartialVMMMem) {
int deviceId = 0;
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
hipDevice_t device;
CTX_CREATE();
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
SECTION("Memory Allocation Type as hipMemAllocationTypePinned") {
prop.type = hipMemAllocationTypePinned;
}
#if HT_AMD
SECTION("Memory Allocation Type as hipMemAllocationTypeUncached") {
prop.type = hipMemAllocationTypeUncached;
}
#endif
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device; // Current Devices
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
// Allocate host memory and intialize data
std::vector<int> A_h(N), B_h(N);
// Initialize with data
for (size_t idx = 0; idx < N; idx++) {
A_h[idx] = idx;
}
// Allocate a bigger physical memory chunk of size_mem
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
// Allocate virtual address range of size twice size_mem
void* ptrA;
HIP_CHECK(hipMemAddressReserve(&ptrA, 2 * size_mem, 0, 0, 0));
hipMemAccessDesc accessDesc = {};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemMap(ptrA, size_mem, 0, handle, 0));
HIP_CHECK(hipMemSetAccess(ptrA, size_mem, &accessDesc, 1));
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptrA), A_h.data(), buffer_size));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptrA), buffer_size));
REQUIRE(true == std::equal(B_h.begin(), B_h.end(), A_h.data()));
HIP_CHECK(hipMemUnmap(ptrA, size_mem));
// Release resources
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(ptrA, 2 * size_mem));
CTX_DESTROY();
}
/**
* Test Description
* ------------------------
* - Negative Argument Tests
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 6.1
*/
HIP_TEST_CASE(Unit_hipMemMap_negative) {
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
CTX_CREATE();
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device; // Current Devices
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
void* ptrA;
// Allocate physical memory
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
// Allocate virtual address range
HIP_CHECK(hipMemAddressReserve(&ptrA, size_mem, 0, 0, 0));
SECTION("nullptr to ptrA") {
REQUIRE(hipMemMap(nullptr, size_mem, 0, handle, 0) == hipErrorInvalidValue);
}
SECTION("pass zero to size") {
REQUIRE(hipMemMap(ptrA, 0, 0, handle, 0) == hipErrorInvalidValue);
}
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(ptrA, size_mem));
CTX_DESTROY();
}
HIP_TEST_CASE(Unit_hipMemMap_Capture) {
hipMemGenericAllocationHandle_t handle;
size_t granularity = 0;
constexpr size_t kAlignment = 2;
constexpr int kDeviceId = 0;
hipDevice_t device = 0;
void* device_ptr = nullptr;
CTX_CREATE();
HIP_CHECK(hipDeviceGet(&device, kDeviceId));
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device;
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
HIP_CHECK(hipMemCreate(&handle, granularity, &prop, 0));
HIP_CHECK(hipMemAddressReserve(&device_ptr, granularity, kAlignment, 0, 0));
hipStream_t stream = nullptr;
HIP_CHECK(hipStreamCreate(&stream));
GENERATE_CAPTURE();
BEGIN_CAPTURE(stream);
HIP_CHECK(hipMemMap(device_ptr, granularity, 0, handle, 0));
END_CAPTURE(stream);
HIP_CHECK(hipStreamDestroy(stream));
HIP_CHECK(hipMemUnmap(device_ptr, granularity));
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(device_ptr, granularity));
}
/**
* Test Description
* ------------------------
* - APU-only. Reserves a VA, creates a physical handle expected to exceed
* the dedicated-VRAM carveout, maps it, grants access, and exercises the
* buffer end-to-end (memset, copy back, verify head and tail). Mirrors
* Unit_hipMalloc_Positive_APU_LargeAllocSpill but through the VMEM API
* path that hipGraphAddMemAllocNode ultimately uses, so it guards the
* same spill behaviour for graph-based allocations.
* Test source
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
*/
HIP_TEST_CASE(Unit_hipMemMap_Positive_APU_LargeAllocSpill) {
hipDeviceProp_t prop{};
HIP_CHECK(hipGetDeviceProperties(&prop, 0));
if (!prop.integrated) {
HIP_SKIP_TEST("dGPU --- APU spill regression test does not apply");
return;
}
int vmm_supported = 0;
HIP_CHECK(hipDeviceGetAttribute(&vmm_supported,
hipDeviceAttributeVirtualMemoryManagementSupported, 0));
if (!vmm_supported) {
HIP_SKIP_TEST("Device does not support virtual memory management");
return;
}
// Assumes the dedicated-VRAM carveout is smaller than 5 GiB.
constexpr size_t requested = static_cast<size_t>(5) << 30;
constexpr size_t headroom = static_cast<size_t>(1) << 30;
if (prop.totalGlobalMem < requested + headroom) {
HIP_SKIP_TEST("APU totalGlobalMem too small for this allocation plus headroom");
return;
}
hipMemAllocationProp aprop{};
aprop.type = hipMemAllocationTypePinned;
aprop.location.type = hipMemLocationTypeDevice;
aprop.location.id = 0;
size_t granularity = 0;
HIP_CHECK(hipMemGetAllocationGranularity(&granularity, &aprop,
hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
const size_t size = ((requested + granularity - 1) / granularity) * granularity;
void* va = nullptr;
HIP_CHECK(hipMemAddressReserve(&va, size, 0, nullptr, 0));
REQUIRE(va != nullptr);
hipMemGenericAllocationHandle_t handle{};
HIP_CHECK(hipMemCreate(&handle, size, &aprop, 0));
REQUIRE(handle != hipMemGenericAllocationHandle_t{});
HIP_CHECK(hipMemMap(va, size, 0, handle, 0));
hipMemAccessDesc access{};
access.location.type = hipMemLocationTypeDevice;
access.location.id = 0;
access.flags = hipMemAccessFlagsProtReadWrite;
HIP_CHECK(hipMemSetAccess(va, size, &access, 1));
constexpr int fill = 0xCD;
HIP_CHECK(hipMemset(va, fill, size));
constexpr size_t sample = static_cast<size_t>(64) * 1024;
std::vector<char> head(sample), tail(sample);
HIP_CHECK(hipMemcpy(head.data(), va, sample, hipMemcpyDeviceToHost));
HIP_CHECK(hipMemcpy(tail.data(), static_cast<char*>(va) + (size - sample), sample,
hipMemcpyDeviceToHost));
for (size_t i = 0; i < sample; ++i) {
REQUIRE(head[i] == static_cast<char>(fill));
REQUIRE(tail[i] == static_cast<char>(fill));
}
HIP_CHECK(hipMemUnmap(va, size));
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(va, size));
}
/**
* Test Description
* ------------------------
* - Characterization test for submitVirtualMap bookkeeping:
* after hipMemMap, the bidirectional cross-link
* (vaddr_sub_obj.phys_mem_obj -> phys, phys.vaddr_mem_obj -> sub_obj)
* must be wired so that hipMemRetainAllocationHandle (which walks
* sub_obj -> phys -> ga) returns the original handle.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 6.1
*/
HIP_TEST_CASE(Unit_hipMemMap_Retrieve_AllocationHandle) {
HIP_CHECK(hipFree(0));
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device;
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
void* ptr = nullptr;
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
HIP_CHECK(hipMemAddressReserve(&ptr, size_mem, 0, 0, 0));
HIP_CHECK(hipMemMap(ptr, size_mem, 0, handle, 0));
// Confirm hipMemRetainAllocationHandle retrieves correct base handle
hipMemGenericAllocationHandle_t retrieved = 0; // 0 instead of nullptr: handle is an integer type on CUDA
HIP_CHECK(hipMemRetainAllocationHandle(&retrieved, ptr));
REQUIRE(retrieved == handle);
HIP_CHECK(hipMemRelease(retrieved));
HIP_CHECK(hipMemUnmap(ptr, size_mem));
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(ptr, size_mem));
}
/**
* Test Description
* ------------------------
* - Characterization test for the multi-alias case of the
* bookkeeping helper. One physical handle mapped to multiple VAs:
* each VA must independently resolve back to the same handle via
* hipMemRetainAllocationHandle.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 6.1
*/
HIP_TEST_CASE(Unit_hipMemMap_Retrieve_MultiAllocationHandle) {
HIP_CHECK(hipFree(0));
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device;
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
void* ptrs[num_buf] = {nullptr, nullptr, nullptr};
for (int i = 0; i < num_buf; ++i) {
HIP_CHECK(hipMemAddressReserve(&ptrs[i], size_mem, 0, 0, 0));
HIP_CHECK(hipMemMap(ptrs[i], size_mem, 0, handle, 0));
}
// Every alias must resolve sub_obj -> phys -> ga back to handle.
for (int i = 0; i < num_buf; ++i) {
hipMemGenericAllocationHandle_t retrieved = 0; // 0 instead of nullptr: handle is an integer type on CUDA
HIP_CHECK(hipMemRetainAllocationHandle(&retrieved, ptrs[i]));
REQUIRE(retrieved == handle);
HIP_CHECK(hipMemRelease(retrieved));
}
for (int i = 0; i < num_buf; ++i) {
HIP_CHECK(hipMemUnmap(ptrs[i], size_mem));
HIP_CHECK(hipMemAddressFree(ptrs[i], size_mem));
}
HIP_CHECK(hipMemRelease(handle));
}
/**
* Test Description
* ------------------------
* - Characterization test for the MemObjMap insertion done by
* FinalizeMapMemObjBookkeeping. After hipMemMap the VA must resolve
* to a backing handle via hipMemRetainAllocationHandle; before
* hipMemMap (only reserved, never mapped) it must not.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 6.1
*/
HIP_TEST_CASE(Unit_hipMemMap_CheckAddMemObj) {
HIP_CHECK(hipFree(0));
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device;
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
hipMemGenericAllocationHandle_t handle;
void* ptr = nullptr;
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
HIP_CHECK(hipMemAddressReserve(&ptr, size_mem, 0, 0, 0));
// Before map: retainAllocationHandle must fail.
hipMemGenericAllocationHandle_t retrieved = 0; // 0 instead of nullptr: handle is an integer type on CUDA
REQUIRE(hipMemRetainAllocationHandle(&retrieved, ptr) == hipErrorInvalidValue);
HIP_CHECK(hipMemMap(ptr, size_mem, 0, handle, 0));
// After map: retainAllocationHandle must pass now ptr has been mapped.
HIP_CHECK(hipMemRetainAllocationHandle(&retrieved, ptr));
REQUIRE(retrieved == handle);
HIP_CHECK(hipMemRelease(retrieved));
HIP_CHECK(hipMemUnmap(ptr, size_mem));
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(ptr, size_mem));
}
/**
* Test Description
* ------------------------
* - hipMemMap test the End-to-end lifecycle: reserve VA,
* create physical handle, hipMemMap, set access, write through HtoD,
* read back via DtoH and a kernel, verify, hipMemUnmap, release, free.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 6.1
*/
HIP_TEST_CASE(Unit_hipMemMap_RoundTrip) {
HIP_CHECK(hipFree(0));
size_t granularity = 0;
size_t buffer_size = N * sizeof(int);
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device;
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
size_t size_mem = ((granularity + buffer_size - 1) / granularity) * granularity;
std::vector<int> A_h(N), B_h(N), C_h(N);
for (size_t idx = 0; idx < N; ++idx) {
A_h[idx] = static_cast<int>(idx);
C_h[idx] = static_cast<int>(idx * idx);
}
hipMemGenericAllocationHandle_t handle;
void* ptr = nullptr;
HIP_CHECK(hipMemCreate(&handle, size_mem, &prop, 0));
HIP_CHECK(hipMemAddressReserve(&ptr, size_mem, 0, 0, 0));
// The direct path under test.
HIP_CHECK(hipMemMap(ptr, size_mem, 0, handle, 0));
hipMemAccessDesc accessDesc{};
accessDesc.location.type = hipMemLocationTypeDevice;
accessDesc.location.id = device;
accessDesc.flags = hipMemAccessFlagsProtReadWrite;
HIP_CHECK(hipMemSetAccess(ptr, size_mem, &accessDesc, 1));
// Data integrity: HtoD then DtoH must round-trip.
std::fill(B_h.begin(), B_h.end(), initializer);
HIP_CHECK(hipMemcpyHtoD(reinterpret_cast<hipDeviceptr_t>(ptr), A_h.data(), buffer_size));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptr), buffer_size));
REQUIRE(std::equal(B_h.begin(), B_h.end(), A_h.data()));
// Kernel write through the mapped VA must be visible to host.
square_kernel<<<dim3(N / threadsPerBlk), dim3(threadsPerBlk), 0, 0>>>(
reinterpret_cast<int*>(ptr));
HIP_CHECK(hipStreamSynchronize(0));
HIP_CHECK(hipMemcpyDtoH(B_h.data(), reinterpret_cast<hipDeviceptr_t>(ptr), buffer_size));
REQUIRE(std::equal(B_h.begin(), B_h.end(), C_h.data()));
// Bookkeeping cross-link must be wired by the direct path (lean on the
// dedicated bookkeeping tests for finer-grained assertions).
hipMemGenericAllocationHandle_t retrieved = 0; // 0 instead of nullptr: handle is an integer type on CUDA
HIP_CHECK(hipMemRetainAllocationHandle(&retrieved, ptr));
REQUIRE(retrieved == handle);
HIP_CHECK(hipMemRelease(retrieved));
HIP_CHECK(hipMemUnmap(ptr, size_mem));
HIP_CHECK(hipMemRelease(handle));
HIP_CHECK(hipMemAddressFree(ptr, size_mem));
}
/**
* Test Description
* ------------------------
* - Backend-agnostic concurrent stress test for the direct virtualMap
* path. N threads each repeatedly reserve/create/map/unmap/release on
* disjoint VAs. A hard wall-clock bound catches deadlock either from
* the HSA lock-free path (no execution() lock) or from PAL execution()
* lock misuse.
* ------------------------
* - unit/virtualMemoryManagement/hipMemMap.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 7.0
*/
HIP_TEST_CASE(Unit_hipMemMap_ConcurrentDisjointMapsNoDeadlock) {
HIP_CHECK(hipFree(0));
size_t granularity = 0;
int deviceId = 0;
hipDevice_t device;
HIP_CHECK(hipDeviceGet(&device, deviceId));
checkVMMSupported(device);
hipMemAllocationProp prop{};
prop.type = hipMemAllocationTypePinned;
prop.location.type = hipMemLocationTypeDevice;
prop.location.id = device;
HIP_CHECK(
hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));
REQUIRE(granularity > 0);
const size_t size_mem = granularity;
constexpr int kThreads = 4;
constexpr int kIters = 16;
std::atomic<bool> failed{false};
auto worker = [&](int tid) {
for (int i = 0; i < kIters && !failed.load(); ++i) {
hipMemGenericAllocationHandle_t h;
void* p = nullptr;
if (hipMemCreate(&h, size_mem, &prop, 0) != hipSuccess) {
failed = true;
return;
}
if (hipMemAddressReserve(&p, size_mem, 0, 0, 0) != hipSuccess) {
(void)hipMemRelease(h);
failed = true;
return;
}
if (hipMemMap(p, size_mem, 0, h, 0) != hipSuccess) {
(void)hipMemAddressFree(p, size_mem);
(void)hipMemRelease(h);
failed = true;
return;
}
hipMemAccessDesc d{};
d.location.type = hipMemLocationTypeDevice;
d.location.id = device;
d.flags = hipMemAccessFlagsProtReadWrite;
(void)hipMemSetAccess(p, size_mem, &d, 1);
if (hipMemUnmap(p, size_mem) != hipSuccess) {
(void)hipMemAddressFree(p, size_mem);
(void)hipMemRelease(h);
failed = true;
return;
}
(void)hipMemRelease(h);
(void)hipMemAddressFree(p, size_mem);
(void)tid;
}
};
// Hard upper bound: 60s. A deadlocked code path would never complete;
// a heavily serialized but live path completes in well under this.
auto t0 = std::chrono::steady_clock::now();
std::vector<std::thread> threads;
threads.reserve(kThreads);
for (int i = 0; i < kThreads; ++i) threads.emplace_back(worker, i);
for (auto& t : threads) t.join();
auto elapsed =
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - t0)
.count();
REQUIRE_FALSE(failed.load());
REQUIRE(elapsed < 60);
}
/**
* End doxygen group VirtualMemoryManagementTest.
* @}
*/