diff options
Diffstat (limited to 'libsmctrl.c')
| -rw-r--r-- | libsmctrl.c | 212 |
1 files changed, 173 insertions, 39 deletions
diff --git a/libsmctrl.c b/libsmctrl.c index 6aa471b..79d2b33 100644 --- a/libsmctrl.c +++ b/libsmctrl.c | |||
| @@ -17,22 +17,27 @@ | |||
| 17 | * Please contact the authors if support is needed for a particular feature on | 17 | * Please contact the authors if support is needed for a particular feature on |
| 18 | * an older CUDA version. Support for those is unimplemented, not impossible. | 18 | * an older CUDA version. Support for those is unimplemented, not impossible. |
| 19 | * | 19 | * |
| 20 | * An old implementation of this file effected the global mask on CUDA 10.2 by | 20 | * An old implementation of this file affected the global mask on CUDA 10.2 by |
| 21 | * changing a field in CUDA's global struct that CUDA applies to the QMD/TMD. | 21 | * changing a field in CUDA's global struct that CUDA applies to the QMD/TMD. |
| 22 | * That implementation was extraordinarily complicated, and was replaced in | 22 | * That implementation was extraordinarily complicated, and was replaced in |
| 23 | * 2024 with a more-backward-compatible way of hooking the TMD/QMD. | 23 | * 2024 with a more-backward-compatible way of hooking the TMD/QMD. |
| 24 | * View the old implementation via Git: `git show aa63a02e:libsmctrl.c`. | 24 | * View the old implementation via Git: `git show aa63a02e:libsmctrl.c`. |
| 25 | */ | 25 | */ |
| 26 | #define _GNU_SOURCE // To enable use of memfd_create() | ||
| 26 | #include <cuda.h> | 27 | #include <cuda.h> |
| 27 | 28 | ||
| 28 | #include <errno.h> | 29 | #include <errno.h> |
| 29 | #include <error.h> | 30 | #include <error.h> |
| 31 | #include <dlfcn.h> | ||
| 30 | #include <fcntl.h> | 32 | #include <fcntl.h> |
| 31 | #include <stdbool.h> | 33 | #include <stdbool.h> |
| 32 | #include <stdint.h> | 34 | #include <stdint.h> |
| 33 | #include <stdio.h> | 35 | #include <stdio.h> |
| 34 | #include <sys/ipc.h> | 36 | #include <string.h> |
| 35 | #include <sys/shm.h> | 37 | #include <sys/mman.h> |
| 38 | #include <sys/socket.h> | ||
| 39 | #include <sys/types.h> | ||
| 40 | #include <sys/un.h> | ||
| 36 | #include <unistd.h> | 41 | #include <unistd.h> |
| 37 | 42 | ||
| 38 | #include "libsmctrl.h" | 43 | #include "libsmctrl.h" |
| @@ -48,20 +53,33 @@ | |||
| 48 | // (No testing attempted on pre-CUDA-6.5 versions) | 53 | // (No testing attempted on pre-CUDA-6.5 versions) |
| 49 | // Values for the following three lines can be extracted by tracing CUPTI as | 54 | // Values for the following three lines can be extracted by tracing CUPTI as |
| 50 | // it interects with libcuda.so to set callbacks. | 55 | // it interects with libcuda.so to set callbacks. |
| 51 | static const CUuuid callback_funcs_id = {0x2c, (char)0x8e, 0x0a, (char)0xd8, 0x07, 0x10, (char)0xab, 0x4e, (char)0x90, (char)0xdd, 0x54, 0x71, (char)0x9f, (char)0xe5, (char)0xf7, 0x4b}; | 56 | static const CUuuid callback_funcs_id = {{0x2c, (char)0x8e, 0x0a, (char)0xd8, 0x07, 0x10, (char)0xab, 0x4e, (char)0x90, (char)0xdd, 0x54, 0x71, (char)0x9f, (char)0xe5, (char)0xf7, 0x4b}}; |
| 52 | // These callback descriptors appear to intercept the TMD/QMD late enough that | 57 | // These callback descriptors appear to intercept the TMD/QMD late enough that |
| 53 | // CUDA has already applied the per-stream mask from its internal data | 58 | // CUDA has already applied the per-stream mask from its internal data |
| 54 | // structures, allowing us to override it with the next mask. | 59 | // structures, allowing us to override it with the next mask. |
| 55 | #define QMD_DOMAIN 0xb | 60 | #define QMD_DOMAIN 0xb |
| 56 | #define QMD_PRE_UPLOAD 0x1 | 61 | #define QMD_PRE_UPLOAD 0x1 |
| 62 | /** | ||
| 63 | * These globals must be non-static (i.e., have global linkage) to ensure that | ||
| 64 | * if multiple copies of the library are loaded (e.g., dynamically linked to | ||
| 65 | * both this program and a dependency), secondary copies do not attempt to | ||
| 66 | * repeat initialization or make changes to unused copies of mask values. | ||
| 67 | */ | ||
| 57 | // Supreme mask (cannot be overridden) | 68 | // Supreme mask (cannot be overridden) |
| 58 | static uint64_t *g_supreme_sm_mask = NULL; | 69 | uint128_t *g_supreme_sm_mask = NULL; |
| 59 | // Global mask (applies across all threads) | 70 | // Global mask (applies across all threads) |
| 60 | static uint64_t g_sm_mask = 0; | 71 | uint64_t g_sm_mask = 0; |
| 61 | // Next mask (applies per-thread) | 72 | // Next mask (applies per-thread) |
| 62 | static __thread uint64_t g_next_sm_mask = 0; | 73 | __thread uint64_t g_next_sm_mask = 0; |
| 63 | // Flag value to indicate if setup has been completed | 74 | // Flag value to indicate if setup has been completed |
| 64 | static bool sm_control_setup_called = false; | 75 | bool sm_control_setup_called = false; |
| 76 | |||
| 77 | #ifdef LIBSMCTRL_STATIC | ||
| 78 | // Special handling for if built as a static library, and the libcuda.so.1 | ||
| 79 | // libsmctrl wrapper is in use (see comment on setup() constructor for detail). | ||
| 80 | static void (*shared_set_global_mask)(uint64_t) = NULL; | ||
| 81 | static void (*shared_set_next_mask)(uint64_t) = NULL; | ||
| 82 | #endif | ||
| 65 | 83 | ||
| 66 | // v1 has been removed---it intercepted the TMD/QMD too early, making it | 84 | // v1 has been removed---it intercepted the TMD/QMD too early, making it |
| 67 | // impossible to override the CUDA-injected stream mask with the next mask. | 85 | // impossible to override the CUDA-injected stream mask with the next mask. |
| @@ -78,7 +96,7 @@ static void control_callback_v2(void *ukwn, int domain, int cbid, const void *in | |||
| 78 | if (!tmd) | 96 | if (!tmd) |
| 79 | abort(1, 0, "TMD allocation appears NULL; likely forward-compatibilty issue.\n"); | 97 | abort(1, 0, "TMD allocation appears NULL; likely forward-compatibilty issue.\n"); |
| 80 | 98 | ||
| 81 | uint32_t *lower_ptr, *upper_ptr; | 99 | uint32_t *lower_ptr, *upper_ptr, *ext_lower_ptr, *ext_upper_ptr; |
| 82 | 100 | ||
| 83 | // The location of the TMD version field seems consistent across versions | 101 | // The location of the TMD version field seems consistent across versions |
| 84 | uint8_t tmd_ver = *(uint8_t*)(tmd + 72); | 102 | uint8_t tmd_ver = *(uint8_t*)(tmd + 72); |
| @@ -87,10 +105,12 @@ static void control_callback_v2(void *ukwn, int domain, int cbid, const void *in | |||
| 87 | // TMD V04_00 is used starting with Hopper to support masking >64 TPCs | 105 | // TMD V04_00 is used starting with Hopper to support masking >64 TPCs |
| 88 | lower_ptr = tmd + 304; | 106 | lower_ptr = tmd + 304; |
| 89 | upper_ptr = tmd + 308; | 107 | upper_ptr = tmd + 308; |
| 108 | ext_lower_ptr = tmd + 312; | ||
| 109 | ext_upper_ptr = tmd + 316; | ||
| 90 | // XXX: Disable upper 64 TPCs until we have ...next_mask_ext and | 110 | // XXX: Disable upper 64 TPCs until we have ...next_mask_ext and |
| 91 | // ...global_mask_ext | 111 | // ...global_mask_ext |
| 92 | *(uint32_t*)(tmd + 312) = -1; | 112 | *ext_lower_ptr = -1; |
| 93 | *(uint32_t*)(tmd + 316) = -1; | 113 | *ext_upper_ptr = -1; |
| 94 | // An enable bit is also required | 114 | // An enable bit is also required |
| 95 | *(uint32_t*)tmd |= 0x80000000; | 115 | *(uint32_t*)tmd |= 0x80000000; |
| 96 | } else if (tmd_ver >= 0x16) { | 116 | } else if (tmd_ver >= 0x16) { |
| @@ -119,6 +139,10 @@ static void control_callback_v2(void *ukwn, int domain, int cbid, const void *in | |||
| 119 | if (g_supreme_sm_mask) { | 139 | if (g_supreme_sm_mask) { |
| 120 | *lower_ptr |= (uint32_t)*g_supreme_sm_mask; | 140 | *lower_ptr |= (uint32_t)*g_supreme_sm_mask; |
| 121 | *upper_ptr |= (uint32_t)(*g_supreme_sm_mask >> 32); | 141 | *upper_ptr |= (uint32_t)(*g_supreme_sm_mask >> 32); |
| 142 | if (tmd_ver >= 0x40) { | ||
| 143 | *ext_lower_ptr |= (uint32_t)(*g_supreme_sm_mask >> 64); | ||
| 144 | *ext_upper_ptr |= (uint32_t)(*g_supreme_sm_mask >> 96); | ||
| 145 | } | ||
| 122 | } | 146 | } |
| 123 | 147 | ||
| 124 | //fprintf(stderr, "Final SM Mask (lower): %x\n", *lower_ptr); | 148 | //fprintf(stderr, "Final SM Mask (lower): %x\n", *lower_ptr); |
| @@ -163,12 +187,26 @@ static void setup_sm_control_callback() { | |||
| 163 | 187 | ||
| 164 | // Set default mask for all launches | 188 | // Set default mask for all launches |
| 165 | void libsmctrl_set_global_mask(uint64_t mask) { | 189 | void libsmctrl_set_global_mask(uint64_t mask) { |
| 190 | #ifdef LIBSMCTRL_STATIC | ||
| 191 | // Special handling for if built as a static library, and the libcuda.so.1 | ||
| 192 | // libsmctrl wrapper is in use (see comment on setup() constructor for | ||
| 193 | // detail). | ||
| 194 | if (shared_set_global_mask) | ||
| 195 | return (*shared_set_global_mask)(mask); | ||
| 196 | #endif | ||
| 166 | setup_sm_control_callback(); | 197 | setup_sm_control_callback(); |
| 167 | g_sm_mask = mask; | 198 | g_sm_mask = mask; |
| 168 | } | 199 | } |
| 169 | 200 | ||
| 170 | // Set mask for next launch from this thread | 201 | // Set mask for next launch from this thread |
| 171 | void libsmctrl_set_next_mask(uint64_t mask) { | 202 | void libsmctrl_set_next_mask(uint64_t mask) { |
| 203 | #ifdef LIBSMCTRL_STATIC | ||
| 204 | // Special handling for if built as a static library, and the libcuda.so.1 | ||
| 205 | // libsmctrl wrapper is in use (see comment on setup() constructor for | ||
| 206 | // detail). | ||
| 207 | if (shared_set_next_mask) | ||
| 208 | return (*shared_set_next_mask)(mask); | ||
| 209 | #endif | ||
| 172 | setup_sm_control_callback(); | 210 | setup_sm_control_callback(); |
| 173 | g_next_sm_mask = mask; | 211 | g_next_sm_mask = mask; |
| 174 | } | 212 | } |
| @@ -248,7 +286,7 @@ struct stream_sm_mask_v2 { | |||
| 248 | // (CUDA 9.0 behaves slightly different on this platform.) | 286 | // (CUDA 9.0 behaves slightly different on this platform.) |
| 249 | // @return 1 if detected, 0 if not, -cuda_err on error | 287 | // @return 1 if detected, 0 if not, -cuda_err on error |
| 250 | #if __aarch64__ | 288 | #if __aarch64__ |
| 251 | int detect_parker_soc() { | 289 | static int detect_parker_soc() { |
| 252 | int cap_major, cap_minor, err, dev_count; | 290 | int cap_major, cap_minor, err, dev_count; |
| 253 | if (err = cuDeviceGetCount(&dev_count)) | 291 | if (err = cuDeviceGetCount(&dev_count)) |
| 254 | return -err; | 292 | return -err; |
| @@ -272,7 +310,7 @@ int detect_parker_soc() { | |||
| 272 | } | 310 | } |
| 273 | #endif // __aarch64__ | 311 | #endif // __aarch64__ |
| 274 | 312 | ||
| 275 | // Should work for CUDA 8.0 through 12.6 | 313 | // Should work for CUDA 8.0 through 12.8 |
| 276 | // A cudaStream_t is a CUstream*. We use void* to avoid a cuda.h dependency in | 314 | // A cudaStream_t is a CUstream*. We use void* to avoid a cuda.h dependency in |
| 277 | // our header | 315 | // our header |
| 278 | void libsmctrl_set_stream_mask(void* stream, uint64_t mask) { | 316 | void libsmctrl_set_stream_mask(void* stream, uint64_t mask) { |
| @@ -417,7 +455,8 @@ void libsmctrl_set_stream_mask_ext(void* stream, uint128_t mask) { | |||
| 417 | } | 455 | } |
| 418 | } | 456 | } |
| 419 | 457 | ||
| 420 | /* INFORMATIONAL FUNCTIONS */ | 458 | |
| 459 | /*** TPC and GPU Informational Functions ***/ | ||
| 421 | 460 | ||
| 422 | // Read an integer from a file in `/proc` | 461 | // Read an integer from a file in `/proc` |
| 423 | static int read_int_procfile(char* filename, uint64_t* out) { | 462 | static int read_int_procfile(char* filename, uint64_t* out) { |
| @@ -590,32 +629,98 @@ abort_cuda: | |||
| 590 | return EIO; | 629 | return EIO; |
| 591 | } | 630 | } |
| 592 | 631 | ||
| 632 | |||
| 633 | /*** Private functions for nvtaskset and building as a libcuda.so.1 wrapper ***/ | ||
| 634 | |||
| 635 | // Check if NVIDIA MPS is running, following the process that `strace` shows | ||
| 636 | // `nvidia-cuda-mps-control` to use. MPS is a prerequisite to co-running | ||
| 637 | // multiple GPU-using tasks without timeslicing. | ||
| 638 | bool libsmctrl_is_mps_running() { | ||
| 639 | char *mps_pipe_dir; | ||
| 640 | int mps_ctrl; | ||
| 641 | struct sockaddr_un mps_ctrl_addr; | ||
| 642 | mps_ctrl_addr.sun_family = AF_UNIX; | ||
| 643 | const int yes = 1; | ||
| 644 | |||
| 645 | if (!(mps_pipe_dir = getenv("CUDA_MPS_PIPE_DIRECTORY"))) | ||
| 646 | mps_pipe_dir = "/tmp/nvidia-mps"; | ||
| 647 | // Pipe names are limited to 108 characters long | ||
| 648 | snprintf(mps_ctrl_addr.sun_path, 108, "%s/control", mps_pipe_dir); | ||
| 649 | // This mirrors the process `nvidia-cuda-mps-control` uses to detect MPS | ||
| 650 | if ((mps_ctrl = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1) | ||
| 651 | return false; | ||
| 652 | if (setsockopt(mps_ctrl, SOL_SOCKET, SO_PASSCRED, &yes, sizeof(yes)) == -1) | ||
| 653 | return false; | ||
| 654 | if (connect(mps_ctrl, &mps_ctrl_addr, sizeof(struct sockaddr_un)) == -1) | ||
| 655 | return false; | ||
