blob: aa84ea524d5de17942aceeb7738f675ce03df23e [file] [log] [blame]
/*****************************************************************************\
* gpu_nrt.c
*****************************************************************************
* Copyright (C) SchedMD LLC.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#define _GNU_SOURCE
#include <dirent.h>
#include "../common/gpu_common.h"
#define NEURON_SYSFS_PREFIX "/sys/devices/virtual/neuron_device/"
#define NEURON_SYSFS_DEVICE_NAME_PREFIX \
NEURON_SYSFS_PREFIX "neuron%d/info/architecture/device_name"
#define NEURON_SYSFS_CONNECTED_DEV_PREFIX \
NEURON_SYSFS_PREFIX "neuron%d/connected_devices"
#define CONNECTED_DEVICES_SZ 100
#define DEVICE_NAME_SZ 50
/*
* These variables are required by the generic plugin interface. If they
* are not found in the plugin, the plugin loader will ignore it.
*
* plugin_name - A string giving a human-readable description of the
* plugin. There is no maximum length, but the symbol must refer to
* a valid string.
*
* plugin_type - A string suggesting the type of the plugin or its
* applicability to a particular form of data or method of data handling.
* If the low-level plugin API is used, the contents of this string are
* unimportant and may be anything. Slurm uses the higher-level plugin
* interface which requires this string to be of the form
*
* <application>/<method>
*
* where <application> is a description of the intended application of
* the plugin (e.g., "auth" for Slurm authentication) and <method> is a
* description of how this plugin satisfies that application. Slurm will
* only load authentication plugins if the plugin_type string has a prefix
* of "auth/".
*
* plugin_version - an unsigned 32-bit integer containing the Slurm version
* (major.minor.micro combined into a single number).
*/
const char plugin_name[] = "GPU NRT plugin";
const char plugin_type[] = "gpu/nrt";
const uint32_t plugin_version = SLURM_VERSION_NUMBER;
static int _count_devices(uint32_t *dev_count)
{
struct dirent *de;
uint32_t dev_num;
DIR *dr = opendir(NEURON_SYSFS_PREFIX);
*dev_count = 0;
if (!dr)
return SLURM_ERROR;
while ((de = readdir(dr))) {
if ((sscanf(de->d_name, "neuron%u\n", &dev_num) == 1)) {
(*dev_count)++;
}
}
closedir(dr);
return SLURM_SUCCESS;
}
static char *_get_device_name(unsigned int dev_inx)
{
FILE *fp = NULL;
char *sysfs_file = NULL;
char *device_name = NULL;
sysfs_file = xstrdup_printf(NEURON_SYSFS_DEVICE_NAME_PREFIX, dev_inx);
fp = fopen(sysfs_file, "r");
if (!fp) {
debug("Could not access device name in Neuron sysfs interface");
xfree(sysfs_file);
return NULL;
}
device_name = xmalloc(DEVICE_NAME_SZ);
if (!fgets(device_name, DEVICE_NAME_SZ, fp))
debug("Could not read Neuron device name");
gpu_common_underscorify_tolower(device_name);
xfree(sysfs_file);
fclose(fp);
return device_name;
}
static bool _is_link(int *link_nums, uint32_t dev_cnt, int dev_inx)
{
for (uint32_t i = 0; i < dev_cnt; i++) {
if (link_nums[i] == dev_inx)
return true;
}
return false;
}
static char *_get_connected_devices(int dev_inx, uint32_t dev_cnt)
{
FILE *fp = NULL;
char *sysfs_file = NULL;
char *tok, *save_ptr;
char conn_dev[CONNECTED_DEVICES_SZ];
int link_nums[CONNECTED_DEVICES_SZ];
char *links = NULL;
int tmp_link;
int num_links = 0;
sysfs_file = xstrdup_printf(NEURON_SYSFS_CONNECTED_DEV_PREFIX, dev_inx);
fp = fopen(sysfs_file, "r");
if (!fp) {
debug("Could not access connected_devices in Neuron sysfs interface");
xfree(sysfs_file);
return NULL;
}
if (!fgets(conn_dev, CONNECTED_DEVICES_SZ, fp)) {
debug("Could not read Neuron connected devices. Setting empty links");
goto endit;
}
/*
* Convert to array of ints for processing
* The link numbers can be in any order
*/
tok = strtok_r(conn_dev, ", ", &save_ptr);
while (tok) {
tmp_link = atoi(tok);
link_nums[num_links++] = tmp_link;
tok = strtok_r(NULL, ", ", &save_ptr);
}
for (uint32_t i = 0; i < dev_cnt; i++) {
if (_is_link(link_nums, num_links, i))
xstrfmtcat(links, "%s%d", i ? "," : "", 1);
else if (i == dev_inx)
xstrfmtcat(links, "%s%d", i ? "," : "", -1);
else
xstrfmtcat(links, "%s%d", i ? "," : "", 0);
}
endit:
xfree(sysfs_file);
fclose(fp);
return links;
}
static list_t *_get_system_gpu_list_neuron(node_config_load_t *node_conf)
{
struct dirent *de;
unsigned int dev_inx;
uint32_t dev_cnt = 0;
list_t *gres_list_system = NULL;
DIR *dr = opendir(NEURON_SYSFS_PREFIX);
if (!dr)
return NULL;
_count_devices(&dev_cnt);
while ((de = readdir(dr))) {
if ((sscanf(de->d_name, "neuron%d\n", &dev_inx) == 1)) {
char *device_file = NULL;
char *links = NULL;
char *device_name = NULL;
gres_slurmd_conf_t gres_slurmd_conf = {
.config_flags = GRES_CONF_AUTODETECT,
.count = 1,
.cpu_cnt = node_conf->cpu_cnt,
.name = "gpu",
};
xstrfmtcat(device_file, "/dev/neuron%u", dev_inx);
device_name = _get_device_name(dev_inx);
links = _get_connected_devices(dev_inx, dev_cnt);
debug2("GPU index %u:", dev_inx);
debug2(" Name: %s", device_name);
debug2(" Links: %s", links);
debug2(" Device File: %s", device_file);
gres_slurmd_conf.type_name = device_name;
gres_slurmd_conf.links = links;
gres_slurmd_conf.file = device_file;
if (!gres_list_system)
gres_list_system =
list_create(destroy_gres_slurmd_conf);
/* Add the GPU to list */
add_gres_to_list(gres_list_system, &gres_slurmd_conf);
xfree(device_file);
xfree(links);
xfree(device_name);
}
}
closedir(dr);
return gres_list_system;
}
extern int init(void)
{
debug("%s: %s loaded", __func__, plugin_name);
return SLURM_SUCCESS;
}
extern void fini(void)
{
debug("%s: unloading %s", __func__, plugin_name);
}
extern void gpu_p_get_device_count(uint32_t *device_count)
{
if (_count_devices(device_count) != SLURM_SUCCESS)
error("Failed to get device count from neuron sysfs interface");
return;
}
extern list_t *gpu_p_get_system_gpu_list(node_config_load_t *node_conf)
{
list_t *gres_list_system = _get_system_gpu_list_neuron(node_conf);
xassert(node_conf);
if (!gres_list_system)
error("System GPU detection failed");
return gres_list_system;
}
extern void gpu_p_step_hardware_init(bitstr_t *usable_gpus, char *tres_freq)
{
return;
}
extern void gpu_p_step_hardware_fini(void)
{
return;
}
extern char *gpu_p_test_cpu_conv(char *cpu_range)
{
return NULL;
}
extern int gpu_p_energy_read(uint32_t dv_ind, gpu_status_t *gpu)
{
return SLURM_SUCCESS;
}
extern int gpu_p_usage_read(pid_t pid, acct_gather_data_t *data)
{
/*
* Currently there is the ability to read memory usage of a device in the
* sysfs interface but it is not PID based and requires adding different
* memory fields for each core for a total value on the device.
*/
/*
*#define NEURON_SYSFS_CORE_CNT_PREFIX \
* NEURON_SYSFS_PREFIX "neuron%d/core_count"
*
*#define NEURON_SYSFS_DEVICE_MEM_PREFIX \
* NEURON_SYSFS_PREFIX
* "neuron%d/neuron_core%d/stats/memory_usage/device_mem/present"
*
*static int gpumem_pos = -1; // Init this in init()
*
*static int _get_core_count(int dev_inx)
*{
* FILE *fp = NULL;
* char *sysfs_file = NULL;
* int dev_core_cnt = 0;
*
* sysfs_file = xstrdup_printf(NEURON_SYSFS_CORE_CNT_PREFIX, dev_inx);
* fp = fopen(sysfs_file, "r");
* if (!fp) {
* debug("Could not access core count in Neuron sysfs interface");
* xfree(sysfs_file);
* return -1;
* }
*
* if (!fscanf(fp, "%d", dev_core_cnt))
* debug("Could not read Neuron core count");
*
* xfree(sysfs_file);
* fclose(fp);
* return dev_core_cnt;
*}
*
*static void _read_mem(int dev_inx, int core_cnt, pid_t pid, acct_gather_data_t *data)
*{
* FILE *fp = NULL;
* char *sysfs_file = NULL;
* int dev_mem = 0;
*
* for (int i = 0; i < core_cnt; i++) {
*
* sysfs_file = xstrdup_printf(NEURON_SYSFS_DEVICE_MEM_PREFIX,
* dev_inx, i);
* fp = fopen(sysfs_file, "r");
* if (!fp) {
* debug("Could not access device memory in Neuron sysfs interface");
* xfree(sysfs_file);
* return;
* }
*
* if (!fscanf(fp, "%d", dev_mem))
* debug("Could not read Neuron device memory for core %d",
* i);
*
* data[gpumem_pos].size_read += dev_mem;
*
* xfree(sysfs_file);
* fclose(fp);
* }
*}
*
*extern int gpu_p_usage_read(pid_t pid, acct_gather_data_t *data)
*{
*
* // TODO: Determine how to incorporate getting acct_gather_data_t for
* // specific pids. As is this code just adds all memory being used across
* // all cores on all devices
*
* uint32_t device_count = 0;
* int core_cnt = 0;
* bool track_gpumem;
*
* track_gpumem = (gpumem_pos != -1);
*
* if (!track_gpumem) {
* debug2("%s: We are not tracking TRES gpumem", __func__);
* return SLURM_SUCCESS;
* }
*
* _count_devices(&device_count);
*
* data[gpumem_pos].size_read = 0;
*
* for (int i = 0; i < device_count; i++) {
*
* if (track_gpumem) {
* core_cnt = _get_core_count(i);
* _read_mem(i, core_cnt, pid, data)
* }
*
* log_flag(JAG, "pid %d has MemMB=%lu",
* pid,
* data[gpumem_pos].size_read / 1048576);
* }
*}
*/
return SLURM_SUCCESS;
}