| /*****************************************************************************\ |
| * 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; |
| } |