==> tests/cuda.c <==1/* cuda.rs against a simulated device: eager loading, primary and green contexts,
2 * limits and register caps, a module's whole life (shape, scratch, globals,
3 * occupancy, launch, query, unload), and the refusals with their messages. */
4#include "mesh/check.h"
5#include "mesh/cuda.h"
6#include "mesh/cuda_fake.h"
7
8#include <stdatomic.h>
9#include <stdlib.h>
10#include <string.h>
11#include <time.h>
12
13static atomic_int ran;
14
15static void kernel(const char *entry, uint64_t mailbox, uint32_t slot, uint32_t nodes, uint32_t threads, void *user) {
16 (void)user;
17 CHECK_S(entry, "mesh_main");
18 _Atomic uint32_t *w = (_Atomic uint32_t *)(uintptr_t)(mailbox + 0x100);
19 atomic_store(w, slot * 1000 + nodes * 10 + threads / 32);
20 struct timespec t = {0, 20000000};
21 nanosleep(&t, NULL);
22 atomic_fetch_add(&ran, 1);
23}
24
25int main(void) {
26 char *err = NULL;
27 /* force_eager refuses another setting. */
28 setenv("CUDA_MODULE_LOADING", "LAZY", 1);
29 CHECK(!mesh_cuda_force_eager(&err));
30 CHECK_S(err, "CUDA_MODULE_LOADING=\"LAZY\": refusing to run; module loading must be EAGER (a lazy load blocks behind resident kernels)");
31 unsetenv("CUDA_MODULE_LOADING");
32 mesh_cuda_gpu g;
33 CHECK(!mesh_cuda_gpu_open(0, &g, &err));
34 CHECK_S(err, "CUDA_MODULE_LOADING is not EAGER: call cuda::force_eager() first");
35 CHECK(mesh_cuda_force_eager(&err));
36 CHECK_S(getenv("CUDA_MODULE_LOADING"), "EAGER");
37
38 /* A driver that loads lazily is refused. */
39 mesh_fake_cuda_install(&(mesh_fake_cuda_cfg){.loading_mode = CU_MODULE_LAZY_LOADING});
40 CHECK(!mesh_cuda_gpu_open(0, &g, &err));
41 CHECK_S(err, "the driver loads modules CU_MODULE_LAZY_LOADING; EAGER is required (CUDA_MODULE_LOADING must be EAGER before cuInit)");
42
43 mesh_fake_cuda_install(&(mesh_fake_cuda_cfg){.host_atomics = 1, .run = kernel});
44 CHECK(mesh_cuda_gpu_open(0, &g, &err));
45 CHECK_S(g.name, "Fake GB10");
46 CHECK(g.cc_major == 12 && g.cc_minor == 1 && g.sms_total == 48 && g.sms == 48 && !g.green && g.blocks_per_sm == 24);
47 CHECK(g.host_atomics);
48 CHECK(g.limits.max_threads == 1024 && g.limits.regs_per_block == 65536 && g.limits.max_shared == 101376);
49 CHECK_I(mesh_fake_cuda_stats_now().primary_ctx, 1);
50 mesh_cuda_gpu_free(&g);
51
52 /* A green context of 8 SMs; an odd split is refused. */
53 CHECK(mesh_cuda_gpu_open(8, &g, &err));
54 CHECK(g.green && g.sms == 8 && g.sms_total == 48);
55 mesh_cuda_stream s;
56 CHECK(mesh_cuda_new_stream(&g, &s, &err));
57 mesh_cuda_stream_destroy(&s);
58 mesh_cuda_gpu_free(&g);
59 CHECK(!mesh_cuda_gpu_open(7, &g, &err));
60 CHECK_S(err, "green context: cannot split 7 SMs off 48");
61
62 /* Register caps and the block limits. */
63 mesh_cuda_limits l = {1024, 65536, 101376};
64 CHECK_U(mesh_cuda_reg_cap(&l, 1), 0);
65 CHECK_U(mesh_cuda_reg_cap(&l, 8), 0);
66 CHECK_U(mesh_cuda_reg_cap(&l, 16), 128);
67 CHECK_U(mesh_cuda_reg_cap(&l, 32), 64);
68 mesh_cuda_limits small = {1024, 16384, 49152};
69 CHECK_U(mesh_cuda_reg_cap(&small, 32), 16);
70 CHECK(mesh_cuda_limits_check(&l, "p", 32, 0x200, &err));
71 CHECK(!mesh_cuda_limits_check(&small, "p", 33, 0x200, &err));
72 CHECK_S(err, "occupancy: p: warps 33 = 1056 threads per node; the device allows 1024 per block");
73 CHECK(!mesh_cuda_limits_check(&small, "p", 1, 50000, &err));
74 CHECK_S(err, "occupancy: p: 50000 bytes of shared memory per node (the runtime's 512 + shared_bytes); the device allows 49152 per block");
75
76 /* A module's life. */
77 CHECK(mesh_cuda_gpu_open(0, &g, &err));
78 uint8_t *mailbox = calloc(1, 0x1000);
79 uint64_t dev = 0;
80 CHECK(mesh_cuda_host_register(&g, mailbox, 0x1000, &dev, &err));
81 CHECK_U(dev, (uintptr_t)mailbox);
82 mesh_cuda_kernel k;
83 const uint8_t cubin[] = "FAKECUBIN entry=mesh_main regs=40 local=128 maxthreads=512 rt_abi=6";
84 CHECK(!mesh_cuda_load_cubin(&g, cubin, "other", &k, &err));
85 CHECK_S(err, "cuModuleGetFunction(other): CUDA_ERROR_NOT_FOUND");
86 CHECK_I(mesh_fake_cuda_stats_now().modules_live, 0);
87 CHECK(mesh_cuda_load_cubin(&g, cubin, "mesh_main", &k, &err));
88 CHECK(k.threads == 32 && k.smem == 0x200 && k.scratch == 0);
89 uint32_t v;
90 CHECK(mesh_cuda_kernel_regs(&k, &v, &err) && v == 40);
91 CHECK(mesh_cuda_kernel_local_bytes(&k, &v, &err) && v == 128);
92 CHECK(mesh_cuda_kernel_global_u32(&k, "MESH_RT_ABI", &v, &err) && v == 6);
93 CHECK(!mesh_cuda_kernel_set_shape(&k, 1024, 0x200, &err));
94 CHECK_S(err, "regs: 40 registers per thread allow 512 threads per block; the node has 1024");
95 CHECK(mesh_cuda_kernel_set_shape(&k, 256, 0x200 + 0x10000, &err));
96 CHECK_I(mesh_fake_cuda_stats_now().opted_in_smem, 0x10200);
97 CHECK(mesh_cuda_kernel_alloc_scratch(&k, 4096, &err));
98 CHECK(k.scratch != 0);
99 for (int i = 0; i < 4096; i++) CHECK_U(((uint8_t *)(uintptr_t)k.scratch)[i] | (i == 4095 ? 0 : 0), 0);
100 CHECK(mesh_cuda_kernel_max_blocks_per_sm(&k, &v, &err) && v == 16);
101 size_t stack;
102 CHECK(mesh_cuda_set_stack_limit(&g, 4096, &err) && mesh_cuda_stack_limit(&g, &stack, &err) && stack == 4096);
103 CHECK(mesh_cuda_new_stream(&g, &s, &err));
104 uint64_t dmb = dev;
105 uint32_t slot = 3;
106 void *params[] = {&dmb, &slot};
107 CHECK(mesh_cuda_launch_on(&g, &s, &k, 2, params, &err));
108 bool done = true;
109 CHECK(mesh_cuda_query(&g, &s, &done, &err) && !done);
110 CHECK(mesh_fake_cuda_idle(2000));
111 CHECK(mesh_cuda_query(&g, &s, &done, &err) && done);
112 CHECK_U(*(uint32_t *)(mailbox + 0x100), 3 * 1000 + 2 * 10 + 8);
113 mesh_fake_cuda_stats fs = mesh_fake_cuda_stats_now();
114 CHECK(fs.last_grid == 2 && fs.last_block == 256 && fs.last_smem == 0x10200 && fs.last_slot == 3);
115 CHECK(mesh_cuda_unload(&g, &k, &err));
116 fs = mesh_fake_cuda_stats_now();
117 CHECK(fs.modules_live == 0 && fs.frees == 1);
118 mesh_cuda_stream_destroy(&s);
119 /* A failed launch carries the driver's name for the error. */
120 mesh_fake_cuda_fail_next("cuModuleLoadData", 200);
121 CHECK(!mesh_cuda_load_cubin(&g, cubin, "mesh_main", &k, &err));
122 CHECK_S(err, "cuModuleLoadData(cubin): CUDA_ERROR_INVALID_IMAGE");
123 free(err);
124 free(mailbox);
125 mesh_cuda_gpu_free(&g);
126 return mesh_check_done();
127}