| /***************************************************************************** \ | |
| * task_cgroup_devices.c - devices cgroup subsystem for task/cgroup | |
| ***************************************************************************** | |
| * Copyright (C) 2011 BULL | |
| * Written by Yiannis Georgiou <yiannis.georgiou@bull.fr> | |
| * | |
| * 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. | |
| \*****************************************************************************/ | |
| #include "config.h" | |
| #define _GNU_SOURCE | |
| #include <glob.h> | |
| #include <limits.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include "slurm/slurm.h" | |
| #include "slurm/slurm_errno.h" | |
| #include "src/common/list.h" | |
| #include "src/common/xstring.h" | |
| #include "src/interfaces/cgroup.h" | |
| #include "src/interfaces/gres.h" | |
| #include "src/interfaces/namespace.h" | |
| #include "src/slurmd/slurmd/slurmd.h" | |
| #include "src/slurmd/slurmstepd/slurmstepd_job.h" | |
| typedef struct handle_dev_args { | |
| cgroup_level_t cgroup_type; | |
| uint32_t taskid; | |
| stepd_step_rec_t *step; | |
| } handle_dev_args_t; | |
| static bool is_first_task = true; | |
| static int _handle_device_access(void *x, void *arg) | |
| { | |
| gres_device_t *gres_device = (gres_device_t *)x; | |
| handle_dev_args_t *handle_args = (handle_dev_args_t *)arg; | |
| cgroup_limits_t limits; | |
| char *dev_id_str; | |
| int rc = SLURM_SUCCESS; | |
| dev_id_str = gres_device_id2str(&gres_device->dev_desc); | |
| if (slurm_conf.debug_flags & DEBUG_FLAG_GRES) { | |
| char *t_str = NULL; | |
| switch (handle_args->cgroup_type) { | |
| case CG_LEVEL_TASK: | |
| t_str = xstrdup_printf("task_%d", handle_args->taskid); | |
| break; | |
| case CG_LEVEL_JOB: | |
| t_str = xstrdup("job"); | |
| break; | |
| case CG_LEVEL_STEP: | |
| t_str = xstrdup("step"); | |
| break; | |
| default: | |
| t_str = xstrdup("unknown"); | |
| break; | |
| } | |
| log_flag(GRES, "%s %s: adding %s(%s)", | |
| t_str, | |
| gres_device->alloc ? "devices.allow" : "devices.deny", | |
| dev_id_str, gres_device->path); | |
| xfree(t_str); | |
| } | |
| cgroup_init_limits(&limits); | |
| limits.allow_device = gres_device->alloc; | |
| limits.device = gres_device->dev_desc; | |
| limits.taskid = handle_args->taskid; | |
| if (cgroup_g_constrain_set(CG_DEVICES, handle_args->cgroup_type, | |
| &limits) != SLURM_SUCCESS) { | |
| error("Unable to set access constraint for device %s(%s)", | |
| dev_id_str, gres_device->path); | |
| rc = SLURM_ERROR; | |
| } | |
| xfree(dev_id_str); | |
| return rc; | |
| } | |
| extern int task_cgroup_devices_init(void) | |
| { | |
| if (cgroup_g_initialize(CG_DEVICES) != SLURM_SUCCESS) { | |
| error("unable to create devices namespace"); | |
| return SLURM_ERROR; | |
| } | |
| return SLURM_SUCCESS; | |
| } | |
| extern int task_cgroup_devices_fini(void) | |
| { | |
| return cgroup_g_step_destroy(CG_DEVICES); | |
| } | |
| extern int task_cgroup_devices_create(stepd_step_rec_t *step) | |
| { | |
| int rc = SLURM_SUCCESS; | |
| pid_t pid; | |
| list_t *job_gres_list = step->job_gres_list; | |
| list_t *step_gres_list = step->step_gres_list; | |
| list_t *device_list = NULL; | |
| handle_dev_args_t handle_args; | |
| if (is_first_task) { | |
| /* Only do once in this plugin. */ | |
| if (cgroup_g_step_create(CG_DEVICES, step) != SLURM_SUCCESS) | |
| return SLURM_ERROR; | |
| is_first_task = false; | |
| } | |
| /* | |
| * If we cannot do bpf calls because we are in a user namespace and we | |
| * do not have a BPF token. | |
| */ | |
| if (!namespace_g_can_bpf(step) && cgroup_g_bpf_get_token() <= 0) { | |
| log_flag(CGROUP, "Skipping job and step device constrain as we are in a user namespace without a BPF token available"); | |
| goto bpf_unavailable; | |
| } | |
| /* Allow or deny access to devices according to job GRES permissions. */ | |
| device_list = gres_g_get_devices(job_gres_list, true, 0, NULL, 0, step); | |
| if (device_list) { | |
| int tmp; | |
| handle_args.cgroup_type = CG_LEVEL_JOB; | |
| handle_args.step = step; | |
| tmp = list_for_each(device_list, _handle_device_access, | |
| &handle_args); | |
| FREE_NULL_LIST(device_list); | |
| if (tmp < 0) { | |
| rc = SLURM_ERROR; | |
| goto fini; | |
| } | |
| if (cgroup_g_constrain_apply(CG_DEVICES, CG_LEVEL_JOB, | |
| NO_VAL) != SLURM_SUCCESS) { | |
| error("Could not apply device constrain to job"); | |
| rc = SLURM_ERROR; | |
| goto fini; | |
| } | |
| } | |
| if ((step->step_id.step_id != SLURM_BATCH_SCRIPT) && | |
| (step->step_id.step_id != SLURM_EXTERN_CONT) && | |
| (step->step_id.step_id != SLURM_INTERACTIVE_STEP) && | |
| (!(step->flags & LAUNCH_EXT_LAUNCHER))) { | |
| /* | |
| * Allow or deny access to devices according to GRES permissions | |
| * for the step. | |
| */ | |
| device_list = gres_g_get_devices(step_gres_list, false, 0, NULL, | |
| 0, step); | |
| if (device_list) { | |
| int tmp; | |
| handle_args.cgroup_type = CG_LEVEL_STEP; | |
| handle_args.step = step; | |
| tmp = list_for_each(device_list, _handle_device_access, | |
| &handle_args); | |
| FREE_NULL_LIST(device_list); | |
| if (tmp < 0) { | |
| rc = SLURM_ERROR; | |
| goto fini; | |
| } | |
| if (cgroup_g_constrain_apply(CG_DEVICES, CG_LEVEL_STEP, | |
| NO_VAL) != SLURM_SUCCESS) { | |
| error("Could not apply device constrain to step"); | |
| rc = SLURM_ERROR; | |
| goto fini; | |
| } | |
| } | |
| } | |
| bpf_unavailable: | |
| /* attach the slurmstepd to the step devices cgroup */ | |
| pid = getpid(); | |
| rc = cgroup_g_step_addto(CG_DEVICES, &pid, 1); | |
| fini: | |
| return rc; | |
| } | |
| extern int task_cgroup_devices_add_pid(stepd_step_rec_t *step, pid_t pid, | |
| uint32_t taskid) | |
| { | |
| /* This plugin constrain devices to task level. */ | |
| return cgroup_g_task_addto(CG_DEVICES, step, pid, taskid); | |
| } | |
| extern int task_cgroup_devices_constrain(stepd_step_rec_t *step, | |
| uint32_t node_tid, | |
| uint32_t global_tid) | |
| { | |
| list_t *device_list = NULL; | |
| handle_dev_args_t handle_args; | |
| /* | |
| * We do not explicitly constrain devices on the task level of these | |
| * specific steps (they all only have 1 task anyway). e.g. an | |
| * salloc --gres=gpu must have access to the allocated GPUs. If we do | |
| * add the pid (e.g. bash) we'd get constrained. | |
| */ | |
| if ((step->step_id.step_id == SLURM_BATCH_SCRIPT) || | |
| (step->step_id.step_id == SLURM_EXTERN_CONT) || | |
| (step->step_id.step_id == SLURM_INTERACTIVE_STEP) || | |
| (step->flags & LAUNCH_EXT_LAUNCHER)) | |
| return SLURM_SUCCESS; | |
| /* | |
| * If we cannot do bpf calls because we are in a user namespace and we | |
| * do not have a BPF token. | |
| */ | |
| if (!namespace_g_can_bpf(step) && cgroup_g_bpf_get_token() <= 0) { | |
| log_flag(CGROUP,"Skipping task %d device constrain as we are in a user namespace without a BPF token available", | |
| global_tid); | |
| return SLURM_SUCCESS; | |
| } | |
| /* | |
| * Apply gres constrains by getting the allowed devices for this task | |
| * from gres plugin. | |
| */ | |
| device_list = gres_g_get_devices(step->step_gres_list, false, | |
| step->accel_bind_type, step->tres_bind, | |
| node_tid, step); | |
| if (device_list) { | |
| int tmp; | |
| handle_args.cgroup_type = CG_LEVEL_TASK; | |
| handle_args.step = step; | |
| handle_args.taskid = global_tid; | |
| tmp = list_for_each(device_list, _handle_device_access, | |
| &handle_args); | |
| FREE_NULL_LIST(device_list); | |
| if (tmp < 0) | |
| return SLURM_ERROR; | |
| if (cgroup_g_constrain_apply(CG_DEVICES, CG_LEVEL_TASK, | |
| global_tid) != SLURM_SUCCESS) { | |
| error("Could not apply device constrain to task %u", | |
| global_tid); | |
| return SLURM_ERROR; | |
| } | |
| } | |
| return SLURM_SUCCESS; | |
| } | |
| extern int task_cgroup_devices_add_extern_pid(pid_t pid) | |
| { | |
| /* Only in the extern step we will not create specific tasks */ | |
| return cgroup_g_step_addto(CG_DEVICES, &pid, 1); | |
| } |