mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
206 lines
6.4 KiB
Plaintext
206 lines
6.4 KiB
Plaintext
// Copyright 2023 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <cuda_runtime.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <random>
|
|
|
|
#include "cuda_test_util.h" // NOLINT(build/include)
|
|
|
|
__global__ void addKernel(std::uint32_t* data) {
|
|
size_t index = blockIdx.x * blockDim.x + threadIdx.x;
|
|
data[index] += static_cast<std::uint32_t>(index);
|
|
}
|
|
|
|
void TestMallocManagedRoundTrip(int device, unsigned int malloc_flags,
|
|
bool prefetch) {
|
|
constexpr size_t kNumBlocks = 32;
|
|
constexpr size_t kNumThreads = 64;
|
|
constexpr size_t kNumElems = kNumBlocks * kNumThreads;
|
|
|
|
std::uint32_t* data = nullptr;
|
|
constexpr size_t kNumBytes = kNumElems * sizeof(*data);
|
|
CHECK_CUDA(cudaMallocManaged(&data, kNumBytes, malloc_flags));
|
|
|
|
// Initialize all elements in the array with a random value on the host.
|
|
std::random_device rd;
|
|
const std::uint32_t init_val =
|
|
std::uniform_int_distribution<std::uint32_t>()(rd);
|
|
for (size_t i = 0; i < kNumElems; i++) {
|
|
data[i] = init_val;
|
|
}
|
|
|
|
if (prefetch) {
|
|
CHECK_CUDA(cudaMemPrefetchAsync(data, kNumBytes, device));
|
|
}
|
|
|
|
// Mutate the array on the device.
|
|
addKernel<<<kNumBlocks, kNumThreads>>>(data);
|
|
CHECK_CUDA(cudaGetLastError());
|
|
CHECK_CUDA(cudaDeviceSynchronize());
|
|
|
|
if (prefetch) {
|
|
CHECK_CUDA(cudaMemPrefetchAsync(data, kNumBytes, cudaCpuDeviceId));
|
|
}
|
|
|
|
// Check that the array has the expected result.
|
|
for (size_t i = 0; i < kNumElems; i++) {
|
|
std::uint32_t want = init_val + static_cast<std::uint32_t>(i);
|
|
if (data[i] != want) {
|
|
std::cout << "data[" << i << "]: got " << data[i] << ", wanted " << want
|
|
<< " = " << init_val << " + " << i << std::endl;
|
|
abort();
|
|
}
|
|
}
|
|
|
|
CHECK_CUDA(cudaFree(data));
|
|
}
|
|
|
|
void TestMallocManagedReadWrite(int device) {
|
|
constexpr size_t kNumBlocks = 32;
|
|
constexpr size_t kNumThreads = 64;
|
|
constexpr size_t kNumElems = kNumBlocks * kNumThreads;
|
|
|
|
std::uint32_t* data = nullptr;
|
|
constexpr size_t kNumBytes = kNumElems * sizeof(*data);
|
|
CHECK_CUDA(cudaMallocManaged(&data, kNumBytes, cudaMemAttachGlobal));
|
|
|
|
// Initialize all elements in the array with a random value on the host.
|
|
std::random_device rd;
|
|
const std::uint32_t init_val =
|
|
std::uniform_int_distribution<std::uint32_t>()(rd);
|
|
for (size_t i = 0; i < kNumElems; i++) {
|
|
data[i] = init_val;
|
|
}
|
|
|
|
// Write the array's contents to a temporary file.
|
|
char filename[] = "/tmp/cudaMallocManagedTest.XXXXXX";
|
|
int fd = mkstemp(filename);
|
|
if (fd < 0) {
|
|
err(1, "mkstemp");
|
|
}
|
|
size_t done = 0;
|
|
while (done < kNumBytes) {
|
|
ssize_t n = write(fd, reinterpret_cast<char*>(data) + done,
|
|
kNumBytes - done);
|
|
if (n >= 0) {
|
|
done += n;
|
|
} else if (n < 0 && errno != EINTR) {
|
|
err(1, "write");
|
|
}
|
|
}
|
|
|
|
// Mutate the array on the device.
|
|
addKernel<<<kNumBlocks, kNumThreads>>>(data);
|
|
CHECK_CUDA(cudaGetLastError());
|
|
CHECK_CUDA(cudaDeviceSynchronize());
|
|
|
|
// Check that the array has the expected result.
|
|
for (size_t i = 0; i < kNumElems; i++) {
|
|
std::uint32_t want = init_val + static_cast<std::uint32_t>(i);
|
|
if (data[i] != want) {
|
|
std::cout << "data[" << i << "]: got " << data[i] << ", wanted " << want
|
|
<< " = " << init_val << " + " << i << std::endl;
|
|
abort();
|
|
}
|
|
}
|
|
|
|
// Read the array's original contents back from the temporary file.
|
|
if (lseek(fd, 0, SEEK_SET) < 0) {
|
|
err(1, "lseek");
|
|
}
|
|
done = 0;
|
|
while (done < kNumBytes) {
|
|
ssize_t n = read(fd, reinterpret_cast<char*>(data) + done,
|
|
kNumBytes - done);
|
|
if (n > 0) {
|
|
done += n;
|
|
} else if (n == 0) {
|
|
errx(1, "read: unexpected EOF after %zu bytes", done);
|
|
} else if (n < 0 && errno != EINTR) {
|
|
err(1, "read");
|
|
}
|
|
}
|
|
|
|
// Check that the array matches what we originally wrote.
|
|
for (size_t i = 0; i < kNumElems; i++) {
|
|
std::uint32_t want = init_val;
|
|
if (data[i] != want) {
|
|
std::cout << "data[" << i << "]: got " << data[i] << ", wanted " << want
|
|
<< " = " << init_val << " + " << i << std::endl;
|
|
abort();
|
|
}
|
|
}
|
|
|
|
// Mutate the array on the device again.
|
|
addKernel<<<kNumBlocks, kNumThreads>>>(data);
|
|
CHECK_CUDA(cudaGetLastError());
|
|
CHECK_CUDA(cudaDeviceSynchronize());
|
|
|
|
// Check that the array has the expected result again.
|
|
for (size_t i = 0; i < kNumElems; i++) {
|
|
std::uint32_t want = init_val + static_cast<std::uint32_t>(i);
|
|
if (data[i] != want) {
|
|
std::cout << "data[" << i << "]: got " << data[i] << ", wanted " << want
|
|
<< " = " << init_val << " + " << i << std::endl;
|
|
abort();
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
CHECK_CUDA(cudaFree(data));
|
|
}
|
|
|
|
int main() {
|
|
int device;
|
|
CHECK_CUDA(cudaGetDevice(&device));
|
|
|
|
std::cout << "Testing cudaMallocManaged(flags=cudaMemAttachGlobal)"
|
|
<< std::endl;
|
|
TestMallocManagedRoundTrip(device, cudaMemAttachGlobal, false);
|
|
|
|
int cma = 0;
|
|
CHECK_CUDA(
|
|
cudaDeviceGetAttribute(&cma, cudaDevAttrConcurrentManagedAccess, device));
|
|
if (!cma) {
|
|
std::cout << "cudaDevAttrConcurrentManagedAccess not available"
|
|
<< std::endl;
|
|
} else {
|
|
std::cout << "Testing cudaMallocManaged(flags=cudaMemAttachGlobal) "
|
|
"with prefetching"
|
|
<< std::endl;
|
|
TestMallocManagedRoundTrip(device, cudaMemAttachGlobal, true);
|
|
std::cout << "Testing cudaMallocManaged(flags=cudaMemAttachHost)"
|
|
<< std::endl;
|
|
TestMallocManagedRoundTrip(device, cudaMemAttachHost, false);
|
|
std::cout << "Testing cudaMallocManaged(flags=cudaMemAttachHost) "
|
|
"with prefetching"
|
|
<< std::endl;
|
|
TestMallocManagedRoundTrip(device, cudaMemAttachHost, true);
|
|
}
|
|
|
|
std::cout << "Testing read/write syscalls on cudaMallocManaged memory"
|
|
<< std::endl;
|
|
TestMallocManagedReadWrite(device);
|
|
|
|
std::cout << "All tests passed" << std::endl;
|
|
return 0;
|
|
}
|