blob: 890dd5288798d0d3f47a43443547b59f922e8550 [file]
/*****************************************************************************\
* xsched.c - kernel cpu affinity handlers
*****************************************************************************
* 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 <stdbool.h>
#include <string.h>
#include "src/common/slurm_protocol_api.h"
#include "src/common/xmalloc.h"
#include "src/common/xsched.h"
extern xcpuset_t *xcpuset_alloc(void)
{
xcpuset_t *new = xgetaffinity(0);
XCPU_ZERO(new);
return new;
}
extern char *task_cpuset_to_str(const xcpuset_t *mask)
{
#if defined(__APPLE__)
fatal("%s: not supported on macOS", __func__);
#else
int base;
bool leading_zeros = true;
char *str = xmalloc((mask->max_cpus / 4) + 1);
char *ptr = str;
for (base = mask->max_cpus - 4; base >= 0; base -= 4) {
char val = 0;
if (XCPU_ISSET(base, mask))
val |= 1;
if (XCPU_ISSET(base + 1, mask))
val |= 2;
if (XCPU_ISSET(base + 2, mask))
val |= 4;
if (XCPU_ISSET(base + 3, mask))
val |= 8;
/* If it's a leading zero, ignore it */
if (leading_zeros && !val)
continue;
*ptr++ = slurm_hex_to_char(val);
/* All zeros from here on out will be written */
leading_zeros = false;
}
/* If the bitmask is all 0s, add a single 0 */
if (leading_zeros)
*ptr++ = '0';
return str;
#endif
}
extern xcpuset_t *task_str_to_cpuset(const char *str)
{
#if defined(__APPLE__)
fatal("%s: not supported on macOS", __func__);
#else
xcpuset_t *mask = NULL;
int len = strlen(str);
const char *ptr = str + len - 1;
int base = 0;
/* skip 0x, it's all hex anyway */
if ((len > 1) && !memcmp(str, "0x", 2L)) {
str += 2;
len -= 2;
}
mask = xcpuset_alloc();
/* Check that hex chars will fit into the xcpuset_t */
if ((len * 4) > mask->max_cpus) {
error("%s: Hex string is too large to convert to cpu_set_t (length %d, max_cpus %zu)",
__func__, len, mask->max_cpus);
xfree(mask);
return NULL;
}
while (ptr >= str) {
char val = slurm_char_to_hex(*ptr);
if (val == (char) -1) {
xfree(mask);
break;
}
if (val & 1)
XCPU_SET(base, mask);
if (val & 2)
XCPU_SET(base + 1, mask);
if (val & 4)
XCPU_SET(base + 2, mask);
if (val & 8)
XCPU_SET(base + 3, mask);
ptr--;
base += 4;
}
return mask;
#endif
}
extern int xsetaffinity(pid_t pid, xcpuset_t *mask)
{
int rval;
#ifdef __FreeBSD__
rval = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
mask->size, &mask->mask);
#else
rval = sched_setaffinity(pid, mask->size, &mask->mask);
#endif
if (rval) {
char *mstr = task_cpuset_to_str(mask);
verbose("sched_setaffinity(%d,%zu,0x%s) failed: %m",
pid, mask->size, mstr);
xfree(mstr);
}
return rval;
}
static int _getaffinity(pid_t pid, xcpuset_t *mask)
{
errno = 0;
/*
* The FreeBSD cpuset API is a superset of the Linux API.
* In addition to PIDs, it supports threads, interrupts,
* jails, and potentially other objects. The first two arguments
* to cpuset_*etaffinity() below indicate that the third argument
* is a PID. -1 indicates the PID of the calling process.
* Linux sched_*etaffinity() uses 0 for this.
*/
#ifdef __FreeBSD__
return cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
mask->size, &mask->mask);
#else
return sched_getaffinity(pid, mask->size, &mask->mask);
#endif
}
extern xcpuset_t *xgetaffinity(pid_t pid)
{
int rval;
static size_t max_cpus = CPU_SETSIZE;
xcpuset_t *mask = NULL;
while (true) {
xrealloc(mask, (2 * sizeof(size_t)) + CPU_ALLOC_SIZE(max_cpus));
mask->max_cpus = max_cpus;
mask->size = CPU_ALLOC_SIZE(max_cpus);
rval = _getaffinity(pid, mask);
if ((rval < 0) && (errno == EINVAL)) {
max_cpus *= 2;
continue;
}
break;
}
if (rval) {
verbose("sched_getaffinity(%d,%zu) failed with status %d",
pid, mask->size, rval);
} else if (get_log_level() >= LOG_LEVEL_DEBUG3) {
char *mstr = task_cpuset_to_str(mask);
debug3("sched_getaffinity(%d) = 0x%s", pid, mstr);
xfree(mstr);
}
return mask;
}
extern int get_assigned_cpu_count(void)
{
int count = -1;
xcpuset_t *mask = xgetaffinity(0);
count = XCPU_COUNT(mask);
xfree(mask);
return count;
}