aboutsummaryrefslogtreecommitdiffstats
path: root/libsmctrl.c
diff options
context:
space:
mode:
authorJoshua Bakita <bakitajoshua@gmail.com>2025-05-05 03:30:13 -0400
committerJoshua Bakita <bakitajoshua@gmail.com>2025-05-09 06:03:11 -0400
commit97e97dc9d55e71edbc2031e6a509a7cc17abe168 (patch)
tree645476af274ed5716204c27057cd6c8c1f4dfca2 /libsmctrl.c
parentc250928930cb5c95bffc878913301f9a5d4efcb7 (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.c')
-rw-r--r--libsmctrl.c163
1 files changed, 146 insertions, 17 deletions
diff --git a/libsmctrl.c b/libsmctrl.c
index 5ee94fb..6aa471b 100644
--- a/libsmctrl.c
+++ b/libsmctrl.c
@@ -31,6 +31,8 @@
31#include <stdbool.h> 31#include <stdbool.h>
32#include <stdint.h> 32#include <stdint.h>
33#include <stdio.h> 33#include <stdio.h>
34#include <sys/ipc.h>
35#include <sys/shm.h>
34#include <unistd.h> 36#include <unistd.h>
35 37
36#include "libsmctrl.h" 38#include "libsmctrl.h"
@@ -52,6 +54,8 @@ static const CUuuid callback_funcs_id = {0x2c, (char)0x8e, 0x0a, (char)0xd8, 0x0
52// structures, allowing us to override it with the next mask. 54// structures, allowing us to override it with the next mask.
53#define QMD_DOMAIN 0xb 55#define QMD_DOMAIN 0xb
54#define QMD_PRE_UPLOAD 0x1 56#define QMD_PRE_UPLOAD 0x1
57// Supreme mask (cannot be overridden)
58static uint64_t *g_supreme_sm_mask = NULL;
55// Global mask (applies across all threads) 59// Global mask (applies across all threads)
56static uint64_t g_sm_mask = 0; 60static uint64_t g_sm_mask = 0;
57// Next mask (applies per-thread) 61// Next mask (applies per-thread)
@@ -110,6 +114,13 @@ static void control_callback_v2(void *ukwn, int domain, int cbid, const void *in
110 *upper_ptr = (uint32_t)(g_sm_mask >> 32); 114 *upper_ptr = (uint32_t)(g_sm_mask >> 32);
111 } 115 }
112 116
117 // No one may override the supreme SM mask; any SMs disabled in it (set
118 // bits) must always remain disabled.
119 if (g_supreme_sm_mask) {
120 *lower_ptr |= (uint32_t)*g_supreme_sm_mask;
121 *upper_ptr |= (uint32_t)(*g_supreme_sm_mask >> 32);
122 }
123
113 //fprintf(stderr, "Final SM Mask (lower): %x\n", *lower_ptr); 124 //fprintf(stderr, "Final SM Mask (lower): %x\n", *lower_ptr);
114 //fprintf(stderr, "Final SM Mask (upper): %x\n", *upper_ptr); 125 //fprintf(stderr, "Final SM Mask (upper): %x\n", *upper_ptr);
115} 126}
@@ -423,13 +434,30 @@ static int read_int_procfile(char* filename, uint64_t* out) {
423 return 0; 434 return 0;
424} 435}
425 436
426// We support up to 64 TPCs, up to 12 GPCs per GPU, and up to 16 GPUs. 437// We support up to 128 TPCs, up to 12 GPCs per GPU, and up to 16 GPUs.
427// TODO: Handle GPUs with greater than 64 TPCs (e.g. some H100 variants) 438#define MAX_GPCS 12
428static uint64_t tpc_mask_per_gpc_per_dev[16][12]; 439static uint64_t tpc_mask_per_gpc_per_dev[16][MAX_GPCS];
440static uint128_t tpc_mask_per_gpc_per_dev_ext[16][MAX_GPCS];
429// Output mask is vtpc-indexed (virtual TPC) 441// Output mask is vtpc-indexed (virtual TPC)
442// Note that this function has to undo _both_ floorsweeping and ID remapping
430int libsmctrl_get_gpc_info(uint32_t* num_enabled_gpcs, uint64_t** tpcs_for_gpc, int dev) { 443int libsmctrl_get_gpc_info(uint32_t* num_enabled_gpcs, uint64_t** tpcs_for_gpc, int dev) {
431 uint32_t i, j, vtpc_idx = 0; 444 int err, i;
432 uint64_t gpc_mask, num_tpc_per_gpc, max_gpcs, gpc_tpc_mask; 445 uint128_t *tpcs_for_gpc_ext;
446 if ((err = libsmctrl_get_gpc_info_ext(num_enabled_gpcs, &tpcs_for_gpc_ext, dev)))
447 return err;
448 for (i = 0; i < *num_enabled_gpcs; i++) {
449 if ((tpcs_for_gpc_ext[i] & -1ull) != tpcs_for_gpc_ext[i])
450 return ERANGE;
451 tpc_mask_per_gpc_per_dev[dev][i] = (uint64_t)tpcs_for_gpc_ext[i];
452 }
453 *tpcs_for_gpc = tpc_mask_per_gpc_per_dev[dev];
454 return 0;
455}
456
457int libsmctrl_get_gpc_info_ext(uint32_t* num_enabled_gpcs, uint128_t** tpcs_for_gpc, int dev) {
458 uint32_t i, j, tpc_id, gpc_id, num_enabled_tpcs, num_configured_tpcs;
459 uint64_t gpc_mask, num_tpc_per_gpc, max_gpcs, gpc_tpc_mask, gpc_tpc_config, total_read = 0;
460 uint128_t tpc_bit;
433 int err; 461 int err;
434 char filename[100]; 462 char filename[100];
435 *num_enabled_gpcs = 0; 463 *num_enabled_gpcs = 0;
@@ -449,43 +477,79 @@ int libsmctrl_get_gpc_info(uint32_t* num_enabled_gpcs, uint64_t** tpcs_for_gpc,
449 snprintf(filename, 100, "/proc/gpu%d/gpc_mask", dev); 477 snprintf(filename, 100, "/proc/gpu%d/gpc_mask", dev);
450 if (err = read_int_procfile(filename, &gpc_mask)) 478 if (err = read_int_procfile(filename, &gpc_mask))
451 return err; 479 return err;
480 // Determine the number of enabled TPCs
452 snprintf(filename, 100, "/proc/gpu%d/num_tpc_per_gpc", dev); 481 snprintf(filename, 100, "/proc/gpu%d/num_tpc_per_gpc", dev);
453 if (err = read_int_procfile(filename, &num_tpc_per_gpc)) 482 if (err = read_int_procfile(filename, &num_tpc_per_gpc))
454 return err; 483 return err;
455 // For each enabled GPC 484 // For each enabled GPC
485 num_enabled_tpcs = 0;
456 for (i = 0; i < max_gpcs; i++) { 486 for (i = 0; i < max_gpcs; i++) {
457 // Skip this GPC if disabled 487 // Skip this GPC if disabled
458 if ((1 << i) & gpc_mask) 488 if ((1 << i) & gpc_mask)
459 continue; 489 continue;
460 (*num_enabled_gpcs)++; 490 (*num_enabled_gpcs)++;
461 // Get the bitstring of TPCs disabled for this GPC 491 // Get the bitstring of TPCs disabled for this physical GPC
462 // Set bit = disabled TPC 492 // Set bit = disabled TPC
463 snprintf(filename, 100, "/proc/gpu%d/gpc%d_tpc_mask", dev, i); 493 snprintf(filename, 100, "/proc/gpu%d/gpc%d_tpc_mask", dev, i);
464 if (err = read_int_procfile(filename, &gpc_tpc_mask)) 494 if (err = read_int_procfile(filename, &gpc_tpc_mask))
465 return err; 495 return err;
466 uint64_t* tpc_mask = &tpc_mask_per_gpc_per_dev[dev][*num_enabled_gpcs - 1]; 496 // Bits greater than the max number of TPCs should be ignored, so only
467 *tpc_mask = 0; 497 // keep the `num_tpc_per_gpc`-count number of lower bits.
468 for (j = 0; j < num_tpc_per_gpc; j++) { 498 gpc_tpc_mask &= -1u >> (64 - num_tpc_per_gpc);
469 // Skip disabled TPCs 499 // Number of enabled TPCs = max - number disabled
470 if ((1 << j) & gpc_tpc_mask) 500 num_enabled_tpcs += num_tpc_per_gpc - __builtin_popcountl(gpc_tpc_mask);
471 continue; 501 }
472 *tpc_mask |= (1ull << vtpc_idx); 502 // Clear any previous mask
473 vtpc_idx++; 503 for (i = 0; i < MAX_GPCS; i++)
504 tpc_mask_per_gpc_per_dev_ext[dev][i] = 0;
505 // For each enabled TPC
506 for (tpc_id = 0; tpc_id < num_enabled_tpcs;) {
507 // Pull mapping for the next set of 4 TPCs
508 snprintf(filename, 100, "/proc/gpu%d/CWD_GPC_TPC_ID%d", dev, tpc_id / 4);
509 if (err = read_int_procfile(filename, &gpc_tpc_config))
510 return err;
511 total_read += gpc_tpc_config;
512 for (j = 0; j < 4 && tpc_id < num_enabled_tpcs; j++, tpc_id++) {
513 // Set the bit for the current TPC
514 tpc_bit = 1;
515 tpc_bit <<= tpc_id;
516 // Determine which GPC the current TPC is associated with
517 // (upper 4 bits of each byte)
518 gpc_id = (gpc_tpc_config >> (j*8 + 4) & 0xfu);
519 // Save mapping
520 tpc_mask_per_gpc_per_dev_ext[dev][gpc_id] |= tpc_bit;
474 } 521 }
475 } 522 }
476 *tpcs_for_gpc = tpc_mask_per_gpc_per_dev[dev]; 523 // Verify each TPC is configured
524 tpc_bit = 0;
525 for (i = 0; i < MAX_GPCS; i++)
526 tpc_bit |= tpc_mask_per_gpc_per_dev_ext[dev][i];
527 num_configured_tpcs = __builtin_popcountl(tpc_bit) + __builtin_popcountl(tpc_bit >> 64);
528 if (num_configured_tpcs != num_enabled_tpcs) {
529 fprintf(stderr, "libsmctrl: Found configuration for only %d TPCs when %d were expected.\n", num_configured_tpcs, num_enabled_tpcs);
530 return EIO;
531 }
532 // Verify that the configuration was not always zero (indicates a powered-
533 // -off GPU).
534 if (total_read == 0) {
535 fprintf(stderr, "libsmctrl: Is GPU on? Configuration registers are all zero.\n");
536 return EIO;
537 }
538
539 *tpcs_for_gpc = tpc_mask_per_gpc_per_dev_ext[dev];
477 return 0; 540 return 0;
478} 541}
479 542
480int libsmctrl_get_tpc_info(uint32_t* num_tpcs, int dev) { 543int libsmctrl_get_tpc_info(uint32_t* num_tpcs, int dev) {
481 uint32_t num_gpcs; 544 uint32_t num_gpcs;
482 uint64_t* tpcs_per_gpc; 545 uint128_t* tpcs_per_gpc;
483 int res; 546 int res;
484 if (res = libsmctrl_get_gpc_info(&num_gpcs, &tpcs_per_gpc, dev)) 547 if (res = libsmctrl_get_gpc_info_ext(&num_gpcs, &tpcs_per_gpc, dev))
485 return res; 548 return res;
486 *num_tpcs = 0; 549 *num_tpcs = 0;
487 for (int gpc = 0; gpc < num_gpcs; gpc++) { 550 for (int gpc = 0; gpc < num_gpcs; gpc++) {
488 *num_tpcs += __builtin_popcountl(tpcs_per_gpc[gpc]); 551 *num_tpcs += __builtin_popcountl(tpcs_per_gpc[gpc]);
552 *num_tpcs += __builtin_popcountl(tpcs_per_gpc[gpc] >> 64);
489 } 553 }
490 return 0; 554 return 0;
491} 555}
@@ -526,3 +590,68 @@ abort_cuda:
526 return EIO; 590 return EIO;
527} 591}
528 592
593// Allow setting a default mask via an environment variable
594// Also enables libsmctrl to be used on unmodified programs via setting:
595// LD_PRELOAD=libsmctrl.so LIBSMCTRL_MASK=<your mask> ./my_program
596// Where "<your mask>" is replaced with a disable mask, optionally prefixed
597// with a ~ to invert it (make it an enable mask).
598__attribute__((constructor)) static void setup(void) {
599 char *end, *mask_str;
600 // If dynamic changes are disabled (due to an error) this variable is
601 // permanently used to store the supreme mask, rather than the SysV shared
602 // memory segment.
603 static uint64_t mask;
604 bool invert = false;
605 int shmid;
606 key_t shm_key;
607
608 mask_str = getenv("LIBSMCTRL_MASK");
609 if (!mask_str)
610 return;
611
612 if (*mask_str == '~') {
613 invert = true;
614 mask_str++;
615 }
616
617 // XXX: Doesn't support 128-bit masks
618 mask = strtoull(mask_str, &end, 0);
619 // Verify we were able to parse the whole string
620 if (*end != '\0')
621 abort(1, EINVAL, "Unable to apply default mask");
622
623 if (invert)
624 mask = ~mask;
625