aboutsummaryrefslogtreecommitdiffstats
path: root/libsmctrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsmctrl.c')
-rw-r--r--libsmctrl.c212
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.
51static 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}; 56static 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)
58static uint64_t *g_supreme_sm_mask = NULL; 69uint128_t *g_supreme_sm_mask = NULL;
59// Global mask (applies across all threads) 70// Global mask (applies across all threads)
60static uint64_t g_sm_mask = 0; 71uint64_t g_sm_mask = 0;
61// Next mask (applies per-thread) 72// Next mask (applies per-thread)
62static __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
64static bool sm_control_setup_called = false; 75bool 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).
80static void (*shared_set_global_mask)(uint64_t) = NULL;
81static 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
165void libsmctrl_set_global_mask(uint64_t mask) { 189void 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
171void libsmctrl_set_next_mask(uint64_t mask) { 202void 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__
251int detect_parker_soc() { 289static 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
278void libsmctrl_set_stream_mask(void* stream, uint64_t mask) { 316void 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`
423static int read_int_procfile(char* filename, uint64_t* out) { 462static 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.
638bool 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;