summaryrefslogtreecommitdiffstats
path: root/tools/test/cuda_drv_test.c
blob: 8995a55f63fcc516afab132bba2765496ca767a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/* cuda_drv_test.c - minimal CUDA driver-API smoke test (dlopen, no toolkit).
 *
 * Run inside the guest. Exercises the paths CUDA needs beyond NVML:
 *   cuInit -> cuDeviceGetCount/Get -> cuCtxCreate -> cuMemAlloc ->
 *   cuMemcpyHtoD/DtoH -> cuMemFree -> cuCtxDestroy
 *
 * Expected output on success: "ALL OK".
 *
 * Status (2026-09-23): cuInit returns 3 (CUDA_ERROR_NOT_INITIALIZED) on the
 * emulated GPU. strace shows every nvidia.ko ioctl succeeding (RM status 0),
 * the driver completing RmInitAdapter and then closing the device, and
 * libcuda opening /dev/nvidia-uvm before /dev/nvidia0 - i.e. the next blocker
 * is the UVM driver (nvidia-uvm.ko), which needs the GPU MMU/VAS model that
 * the GSP-RM emulation does not provide yet. Build in the guest with:
 *   gcc -O1 -o /tmp/cdt cuda_drv_test.c -ldl
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdint.h>

typedef int CUresult;
typedef int CUdevice;
typedef void *CUcontext;
typedef uint64_t CUdeviceptr;

/* On failure: print cuGetErrorName/String and, when CDT_HOLD=<seconds> is
 * set, keep the process alive so the live state (trackers, RM error record)
 * can be inspected with gdb after cuCtxCreate has given up. */
static void fail_info(CUresult r)
{
    const char *(*errname)(CUresult, const char **) = dlsym(RTLD_DEFAULT, "cuGetErrorName");
    const char *(*errstr)(CUresult, const char **) = dlsym(RTLD_DEFAULT, "cuGetErrorString");
    const char *s = NULL;
    const char *hold = getenv("CDT_HOLD");

    if (errname && errname(r, &s) == 0 && s)
        printf("cuGetErrorName: %s\n", s);
    if (errstr && errstr(r, &s) == 0 && s)
        printf("cuGetErrorString: %s\n", s);
    if (hold && atoi(hold) > 0) {
        printf("CDT_HOLD: sleeping %d s for inspection\n", atoi(hold));
        fflush(stdout);
        sleep((unsigned)atoi(hold));
    }
}

#define CHECK(expr) do { \
    CUresult r = (expr); \
    if (r) { printf("%-24s FAILED rc=%d\n", #expr, r); fail_info(r); return 1; } \
    printf("%-24s OK\n", #expr); \
} while (0)

/* Print before entering libcuda so a crash identifies the call. */
#define MARK(name) do { printf("-> %s\n", name); } while (0)

int main(void)
{
    setvbuf(stdout, NULL, _IONBF, 0);

    void *h = dlopen("libcuda.so.1", RTLD_NOW);
    if (!h) { printf("dlopen libcuda.so.1 failed: %s\n", dlerror()); return 1; }

    CUresult (*cuInit)(unsigned int) = dlsym(h, "cuInit");
    CUresult (*cuDeviceGetCount)(int *) = dlsym(h, "cuDeviceGetCount");
    CUresult (*cuDeviceGet)(CUdevice *, int) = dlsym(h, "cuDeviceGet");
    CUresult (*cuCtxCreate)(CUcontext *, unsigned int, CUdevice) = dlsym(h, "cuCtxCreate_v2");
    CUresult (*cuMemAlloc)(CUdeviceptr *, size_t) = dlsym(h, "cuMemAlloc_v2");
    CUresult (*cuMemcpyHtoD)(CUdeviceptr, const void *, size_t) = dlsym(h, "cuMemcpyHtoD_v2");
    CUresult (*cuMemcpyDtoH)(void *, CUdeviceptr, size_t) = dlsym(h, "cuMemcpyDtoH_v2");
    CUresult (*cuMemFree)(CUdeviceptr) = dlsym(h, "cuMemFree_v2");
    CUresult (*cuCtxDestroy)(CUcontext) = dlsym(h, "cuCtxDestroy_v2");

    if (!cuInit || !cuDeviceGetCount || !cuDeviceGet || !cuCtxCreate ||
        !cuMemAlloc || !cuMemcpyHtoD || !cuMemcpyDtoH || !cuMemFree || !cuCtxDestroy) {
        printf("dlsym failed\n");
        return 1;
    }

    int count = 0;
    CUdevice dev;
    CUcontext ctx = NULL;
    CUdeviceptr dptr = 0;
    unsigned int buf[64];
    unsigned int back[64];
    size_t i;

    MARK("cuInit");
    CHECK(cuInit(0));
    MARK("cuDeviceGetCount");
    CHECK(cuDeviceGetCount(&count));
    printf("device count = %d\n", count);
    if (count < 1) return 1;

    MARK("cuDeviceGet");
    CHECK(cuDeviceGet(&dev, 0));
    printf("device 0 = %d\n", dev);

    MARK("cuCtxCreate");
    CHECK(cuCtxCreate(&ctx, 0, dev));
    printf("context = %p\n", ctx);

    MARK("cuMemAlloc");
    CHECK(cuMemAlloc(&dptr, sizeof(buf)));
    printf("device ptr = 0x%llx (%zu bytes)\n", (unsigned long long)dptr, sizeof(buf));

    for (i = 0; i < 64; i++) buf[i] = (unsigned int)(i * 3 + 1);
    MARK("cuMemcpyHtoD");
    CHECK(cuMemcpyHtoD(dptr, buf, sizeof(buf)));
    memset(back, 0, sizeof(back));
    MARK("cuMemcpyDtoH");
    CHECK(cuMemcpyDtoH(back, dptr, sizeof(buf)));
    printf("roundtrip %s (back[7]=%u expected 22)\n",
           memcmp(buf, back, sizeof(buf)) ? "MISMATCH" : "match", back[7]);

    MARK("cuMemFree");
    CHECK(cuMemFree(dptr));
    MARK("cuCtxDestroy");
    CHECK(cuCtxDestroy(ctx));
    printf("ALL OK\n");
    return 0;
}