1- // XBAR L2 data-integrity checker (CUDA source).
1+ // XBAR L2 data-integrity checker (CUDA source, full host + device ).
22//
3- // This is the original kernel published by Loong0x00 in LACT issue #1147:
4- // https://github.com/ilya-zlobintsev/LACT/issues/1147
3+ // This is the original L2 checker published by Loong0x00 in LACT issue #1147
4+ // (https://github.com/ilya-zlobintsev/LACT/issues/1147), with a small
5+ // maintainable host wrapper:
6+ // - --mb controls the checker buffer size (default 32 MiB).
7+ // - Error messages include the numeric CUDA error code so failures such as
8+ // "cudaMalloc data: (null)" on some systems are actionable instead of
9+ // opaque.
10+ // - load mode runs a concurrent stress kernel to add L2/global traffic.
511//
6- // The bundled xbar_l2_check.exe is built from this source. Keeping the source
7- // in the repository makes the trust chain auditable .
12+ // The PTX-specific L2 load is unchanged:
13+ // ld.global.cg.u32 bypasses L1 allocation and exercises the L2/global path .
814
915#include < cstdint>
16+ #include < cstdio>
17+ #include < cstdlib>
18+ #include < cstring>
19+ #include < chrono>
20+ #include < string>
1021
11- __device__ __forceinline__ uint32_t pattern (uint32_t i) {
22+ #include < cuda_runtime.h>
23+
24+ __host__ __device__ __forceinline__ uint32_t pattern (uint32_t i) {
1225 return i * 747796405u + 2891336453u ;
1326}
1427
@@ -24,3 +37,168 @@ __global__ void xbar_l2_check(const uint32_t *data, uint32_t mask,
2437 atomicAdd (errors, 1ULL );
2538 }
2639}
40+
41+ __global__ void stress_kernel (uint32_t *dummy, uint32_t mask,
42+ uint32_t stress_iters) {
43+ uint32_t s = blockIdx .x * blockDim .x + threadIdx .x ;
44+ for (uint32_t n = 0 ; n < stress_iters; ++n) {
45+ s = s * 1664525u + 1013904223u ;
46+ uint32_t i = s & mask;
47+ dummy[i] = dummy[i] * 1664525u + 1013904223u ;
48+ }
49+ }
50+
51+ static int gpu_blocks = 1360 ;
52+ static int gpu_threads = 256 ;
53+ static int gpu_rounds = 1 ;
54+ static std::string gpu_mode = " idle" ;
55+ static int stress_iters = 100000 ;
56+ static int mb = 32 ;
57+
58+ static bool parse_args (int argc, char **argv) {
59+ for (int i = 1 ; i < argc; ++i) {
60+ std::string arg = argv[i];
61+ auto next = [&](const char *name) -> const char * {
62+ if (i + 1 >= argc) {
63+ fprintf (stderr, " missing value for %s\n " , name);
64+ return nullptr ;
65+ }
66+ return argv[++i];
67+ };
68+ if (arg == " --blocks" ) {
69+ const char *v = next (" --blocks" );
70+ if (!v) return false ;
71+ gpu_blocks = atoi (v);
72+ if (gpu_blocks <= 0 ) { fprintf (stderr, " bad --blocks\n " ); return false ; }
73+ } else if (arg == " --threads" ) {
74+ const char *v = next (" --threads" );
75+ if (!v) return false ;
76+ gpu_threads = atoi (v);
77+ if (gpu_threads <= 0 ) { fprintf (stderr, " bad --threads\n " ); return false ; }
78+ } else if (arg == " --rounds" ) {
79+ const char *v = next (" --rounds" );
80+ if (!v) return false ;
81+ gpu_rounds = atoi (v);
82+ if (gpu_rounds <= 0 ) { fprintf (stderr, " bad --rounds\n " ); return false ; }
83+ } else if (arg == " --mode" ) {
84+ const char *v = next (" --mode" );
85+ if (!v) return false ;
86+ gpu_mode = v;
87+ if (gpu_mode != " idle" && gpu_mode != " load" ) {
88+ fprintf (stderr, " bad --mode (idle|load)\n " );
89+ return false ;
90+ }
91+ } else if (arg == " --stress-iters" ) {
92+ const char *v = next (" --stress-iters" );
93+ if (!v) return false ;
94+ stress_iters = atoi (v);
95+ if (stress_iters <= 0 ) { fprintf (stderr, " bad --stress-iters\n " ); return false ; }
96+ } else if (arg == " --mb" ) {
97+ const char *v = next (" --mb" );
98+ if (!v) return false ;
99+ mb = atoi (v);
100+ if (mb <= 0 || mb > 4096 ) { fprintf (stderr, " bad --mb\n " ); return false ; }
101+ } else {
102+ fprintf (stderr, " unknown arg: %s\n " , arg.c_str ());
103+ return false ;
104+ }
105+ }
106+ return true ;
107+ }
108+
109+ static const char *err_string (cudaError_t err) {
110+ // cudaGetErrorString can return NULL for a not-yet-initialized runtime on
111+ // some systems; fall back to a numeric representation.
112+ const char *s = cudaGetErrorString (err);
113+ return s ? s : " (unknown error)" ;
114+ }
115+
116+ #define CUDA_CHECK (label, expr ) \
117+ do { \
118+ cudaError_t err = (expr); \
119+ if (err != cudaSuccess) { \
120+ fprintf (stderr, " CUDA error: %s: %s [code=%d]\n " , \
121+ label, err_string (err), (int )err); \
122+ return 1 ; \
123+ } \
124+ } while (0 )
125+
126+ int main (int argc, char **argv) {
127+ if (!parse_args (argc, argv))
128+ return 2 ;
129+
130+ size_t bytes = (size_t )mb * 1024 * 1024 ;
131+ size_t words = bytes / sizeof (uint32_t );
132+ if ((words & (words - 1 )) != 0 ) {
133+ fprintf (stderr, " --mb must make a power-of-two word count\n " );
134+ return 2 ;
135+ }
136+ uint32_t mask = (uint32_t )(words - 1 );
137+
138+ uint32_t *host_data = (uint32_t *)malloc (bytes);
139+ if (!host_data) {
140+ fprintf (stderr, " host malloc failed\n " );
141+ return 1 ;
142+ }
143+ for (size_t i = 0 ; i < words; ++i)
144+ host_data[i] = pattern ((uint32_t )i);
145+
146+ uint32_t *data = nullptr ;
147+ CUDA_CHECK (" cudaMalloc data" , cudaMalloc (&data, bytes));
148+ unsigned long long *errors = nullptr ;
149+ CUDA_CHECK (" cudaMalloc errors" , cudaMalloc (&errors, sizeof (*errors)));
150+ uint32_t *dummy = nullptr ;
151+ if (gpu_mode == " load" ) {
152+ size_t dummy_words = (size_t )gpu_blocks * gpu_threads;
153+ if (dummy_words == 0 )
154+ dummy_words = 1 ;
155+ size_t dummy_bytes = dummy_words * sizeof (uint32_t );
156+ CUDA_CHECK (" cudaMalloc dummy" , cudaMalloc (&dummy, dummy_bytes));
157+ CUDA_CHECK (" cudaMemset dummy" , cudaMemset (dummy, 0 , dummy_bytes));
158+ }
159+
160+ CUDA_CHECK (" cudaMemcpy data" , cudaMemcpy (data, host_data, bytes, cudaMemcpyHostToDevice));
161+ CUDA_CHECK (" cudaMemset errors" , cudaMemset (errors, 0 , sizeof (*errors)));
162+
163+ cudaStream_t stream_a = nullptr , stream_b = nullptr ;
164+ if (gpu_mode == " load" ) {
165+ CUDA_CHECK (" cudaStreamCreate" , cudaStreamCreateWithFlags (&stream_a, cudaStreamNonBlocking));
166+ CUDA_CHECK (" cudaStreamCreate" , cudaStreamCreateWithFlags (&stream_b, cudaStreamNonBlocking));
167+ }
168+
169+ auto start = std::chrono::high_resolution_clock::now ();
170+ for (int round = 0 ; round < gpu_rounds; ++round) {
171+ CUDA_CHECK (" cudaMemset errors" , cudaMemset (errors, 0 , sizeof (*errors)));
172+ if (gpu_mode == " load" ) {
173+ size_t dummy_words = (size_t )gpu_blocks * gpu_threads;
174+ uint32_t dummy_mask = (uint32_t )(dummy_words - 1 );
175+ stress_kernel<<<gpu_blocks, gpu_threads, 0 , stream_a>>> (
176+ dummy, dummy_mask, (uint32_t )stress_iters);
177+ CUDA_CHECK (" stress launch" , cudaGetLastError ());
178+ xbar_l2_check<<<gpu_blocks, gpu_threads, 0 , stream_b>>> (
179+ data, mask, errors);
180+ CUDA_CHECK (" checker launch" , cudaGetLastError ());
181+ CUDA_CHECK (" stress sync" , cudaStreamSynchronize (stream_a));
182+ CUDA_CHECK (" device sync" , cudaStreamSynchronize (stream_b));
183+ } else {
184+ xbar_l2_check<<<gpu_blocks, gpu_threads>>> (data, mask, errors);
185+ CUDA_CHECK (" checker launch" , cudaGetLastError ());
186+ CUDA_CHECK (" device sync" , cudaDeviceSynchronize ());
187+ }
188+ unsigned long long host_errors = 0 ;
189+ CUDA_CHECK (" cudaMemcpy errors" ,
190+ cudaMemcpy (&host_errors, errors, sizeof (host_errors), cudaMemcpyDeviceToHost));
191+ auto end = std::chrono::high_resolution_clock::now ();
192+ double elapsed_ms = std::chrono::duration<double , std::milli>(end - start).count ();
193+ printf (" round=%u mode=%s errors=%llu elapsed_ms=%.3f\n " ,
194+ round, gpu_mode.c_str (), host_errors, elapsed_ms);
195+ }
196+
197+ if (stream_a) cudaStreamDestroy (stream_a);
198+ if (stream_b) cudaStreamDestroy (stream_b);
199+ if (dummy) cudaFree (dummy);
200+ cudaFree (errors);
201+ cudaFree (data);
202+ free (host_data);
203+ return 0 ;
204+ }
0 commit comments