diff options
| author | Joshua Bakita <bakitajoshua@gmail.com> | 2025-05-05 03:30:13 -0400 |
|---|---|---|
| committer | Joshua Bakita <bakitajoshua@gmail.com> | 2025-05-09 06:03:11 -0400 |
| commit | 97e97dc9d55e71edbc2031e6a509a7cc17abe168 (patch) | |
| tree | 645476af274ed5716204c27057cd6c8c1f4dfca2 /libsmctrl_test_gpc_info.c | |
| parent | c250928930cb5c95bffc878913301f9a5d4efcb7 (diff) | |
Major update for ECRTS'25: fix TPC to GPU mapping and add a "supreme" mask
These updates are featured in the paper:
J. Bakita and J. H. Anderson, “Hardware Compute Partitioning on
NVIDIA GPUs for Composable Systems”, Proceedings of the 37th
Euromicro Conference on Real-Time Systems (ECRTS), to appear,
Jul 2025.
They:
1. Fix reported GPC to TPC mappings (requires nvdebug update).
2. Add support for a "supreme" mask, which overrides all others and
can be set on a per-process basis via an environment variable,
and optionally modified at runtime via the nvtaskset utility.
3. Add test for the supreme mask.
Diffstat (limited to 'libsmctrl_test_gpc_info.c')
| -rw-r--r-- | libsmctrl_test_gpc_info.c | 72 |
1 files changed, 64 insertions, 8 deletions
diff --git a/libsmctrl_test_gpc_info.c b/libsmctrl_test_gpc_info.c index 9b2b963..558b80a 100644 --- a/libsmctrl_test_gpc_info.c +++ b/libsmctrl_test_gpc_info.c | |||
| @@ -1,7 +1,18 @@ | |||
| 1 | // Copyright 2024 Joshua Bakita | 1 | // Copyright 2024-2025 Joshua Bakita |
| 2 | // Obtain and print the correspondence between TPCs and GPCs for a given GPU. | ||
| 3 | // | ||
| 4 | // Known issues: | ||
| 5 | // - If CUDA cannot see the same number of GPUs as the nvdebug kernel module, | ||
| 6 | // the passed GPU ID may not properly correspond to to an ID an CUDA. This | ||
| 7 | // will cause us to fail to initialize a context on the right device, and | ||
| 8 | // may cause the test to terminate due to no initialized context. This should | ||
| 9 | // only happen if some of the attached GPUs are too old or new for CUDA. | ||
| 2 | #define _GNU_SOURCE | 10 | #define _GNU_SOURCE |
| 11 | #include <cuda.h> | ||
| 12 | |||
| 3 | #include <error.h> | 13 | #include <error.h> |
| 4 | #include <errno.h> | 14 | #include <errno.h> |
| 15 | #include <math.h> | ||
| 5 | #include <stdio.h> | 16 | #include <stdio.h> |
| 6 | #include <stdint.h> | 17 | #include <stdint.h> |
| 7 | #include <stdlib.h> | 18 | #include <stdlib.h> |
| @@ -11,9 +22,9 @@ | |||
| 11 | 22 | ||
| 12 | int main(int argc, char** argv) { | 23 | int main(int argc, char** argv) { |
| 13 | uint32_t num_gpcs = 0, num_tpcs = 0; | 24 | uint32_t num_gpcs = 0, num_tpcs = 0; |
| 14 | uint64_t* masks = NULL; | 25 | uint128_t* masks = NULL; |
| 15 | int res; | 26 | int res, print_width, gpu_id; |
| 16 | int gpu_id = 0; | 27 | CUcontext ctx; |
| 17 | // Optionally support specifying the GPU ID to query via an argument | 28 | // Optionally support specifying the GPU ID to query via an argument |
| 18 | // Important: This GPU ID must match the ID used by the nvdebug module. See | 29 | // Important: This GPU ID must match the ID used by the nvdebug module. See |
| 19 | // the documentation on libsmctrl_get_gpc_info() for details. | 30 | // the documentation on libsmctrl_get_gpc_info() for details. |
| @@ -23,12 +34,57 @@ int main(int argc, char** argv) { | |||
| 23 | } | 34 | } |
| 24 | if (argc > 1) | 35 | if (argc > 1) |
| 25 | gpu_id = atoi(argv[1]); | 36 | gpu_id = atoi(argv[1]); |
| 26 | if ((res = libsmctrl_get_gpc_info(&num_gpcs, &masks, gpu_id)) != 0) | 37 | else |
| 27 | error(1, res, "libsmctrl_get_gpc_info() failed"); | 38 | gpu_id = 0; |
| 39 | // Tell CUDA to use PCI device id ordering (to match nvdebug) | ||
| 40 | putenv((char*)"CUDA_DEVICE_ORDER=PCI_BUS_ID"); | ||
| 41 | // A CUDA context is required before reading the topology information | ||
| 42 | if ((res = cuInit(0))) { | ||
| 43 | const char* name; | ||
| 44 | cuGetErrorName(res, &name); | ||
| 45 | fprintf(stderr, "%s: Unable to initialize CUDA, error %s\n", program_invocation_name, name); | ||
| 46 | return 1; | ||
| 47 | } | ||
| 48 | if ((res = cuCtxCreate(&ctx, 0, 0))) { | ||
| 49 | const char* name; | ||
| 50 | cuGetErrorName(res, &name); | ||
| 51 | fprintf(stderr, "%s: Unable to create a CUDA context, error %s\n", program_invocation_name, name); | ||
| 52 | return 1; | ||
| 53 | } | ||
| 54 | // Pull topology information from libsmctrl | ||
| 55 | if ((res = libsmctrl_get_gpc_info_ext(&num_gpcs, &masks, gpu_id)) != 0) { | ||
| 56 | error(0, res, "libsmctrl_get_gpc_info() failed"); | ||
| 57 | if (res == ENOENT) | ||
| 58 | fprintf(stderr, "%s: Is the nvdebug kernel module loaded?\n", program_invocation_name); | ||
| 59 | if (res == EIO) | ||
| 60 | fprintf(stderr, "%s: Is the GPU powered on, i.e., is there an active context?\n", program_invocation_name); | ||
| 61 | return 1; | ||
| 62 | } | ||
| 28 | printf("%s: GPU%d has %d enabled GPCs.\n", program_invocation_name, gpu_id, num_gpcs); | 63 | printf("%s: GPU%d has %d enabled GPCs.\n", program_invocation_name, gpu_id, num_gpcs); |
| 64 | // Determine how wide the print should be (for pretty-printing) | ||
| 65 | print_width = 0; | ||
| 66 | for (int i = 0; i < num_gpcs; i++) { | ||
| 67 | int shift = 0; | ||
| 68 | while (masks[i] >> shift) | ||
| 69 | shift++; | ||
| 70 | if (shift > print_width) | ||
| 71 | print_width = shift; | ||
| 72 | } | ||
| 73 | // Convert the width to a number of octets, rather than number of bits | ||
| 74 | // (Result of integer divison, +1 if it does not evenly divide) | ||
| 75 | print_width = print_width/4 + !!(print_width % 4); | ||
| 29 | for (int i = 0; i < num_gpcs; i++) { | 76 | for (int i = 0; i < num_gpcs; i++) { |
| 30 | num_tpcs += __builtin_popcountl(masks[i]); | 77 | // No built-in for 128-bit integers, so split it into two 64-bit ones |
| 31 | printf("%s: Mask of %d TPCs associated with GPC %d: %#018lx\n", program_invocation_name, __builtin_popcountl(masks[i]), i, masks[i]); | 78 | int num_tpcs_local = __builtin_popcountl(masks[i]) + __builtin_popcountl(masks[i] >> 64); |
| 79 | num_tpcs += num_tpcs_local; | ||
| 80 | if (print_width > 16) | ||
| 81 | printf("%s: Mask of %d TPCs associated with GPC %d: 0x%0*lx%016lx\n", | ||
| 82 | program_invocation_name, num_tpcs_local, i, print_width - 16, | ||
| 83 | (uint64_t)(masks[i] >> 64), (uint64_t)masks[i]); | ||
| 84 | else | ||
| 85 | printf("%s: Mask of %d TPCs associated with GPC %d: 0x%0*lx\n", | ||
| 86 | program_invocation_name, num_tpcs_local, i, print_width, | ||
| 87 | (uint64_t)masks[i]); | ||
| 32 | } | 88 | } |
| 33 | printf("%s: Total of %u enabled TPCs.\n", program_invocation_name, num_tpcs); | 89 | printf("%s: Total of %u enabled TPCs.\n", program_invocation_name, num_tpcs); |
| 34 | return 0; | 90 | return 0; |
