|  | /*****************************************************************************\ | 
|  | *  slurm_resource_info.c - Functions to determine number of available resources | 
|  | ***************************************************************************** | 
|  | *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P. | 
|  | *  Written by Susanne M. Balle, <susanne.balle@hp.com> | 
|  | *  CODE-OCEC-09-009. All rights reserved. | 
|  | * | 
|  | *  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 <ctype.h> | 
|  | #include <limits.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include "slurm/slurm.h" | 
|  | #include "slurm/slurm_errno.h" | 
|  |  | 
|  | #include "src/common/log.h" | 
|  | #include "src/common/read_config.h" | 
|  | #include "src/common/slurm_protocol_api.h" | 
|  | #include "src/common/slurm_resource_info.h" | 
|  | #include "src/common/xmalloc.h" | 
|  | #include "src/common/xstring.h" | 
|  |  | 
|  | /* | 
|  | * First clear all of the bits in "*data" which are set in "clear_mask". | 
|  | * Then set all of the bits in "*data" that are set in "set_mask". | 
|  | */ | 
|  | static void _clear_then_set(int *data, int clear_mask, int set_mask) | 
|  | { | 
|  | *data &= ~clear_mask; | 
|  | *data |= set_mask; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * _isvalue | 
|  | * returns 1 is the argument appears to be a value, 0 otherwise | 
|  | */ | 
|  | static int _isvalue(char *arg) { | 
|  | if (isdigit((int)*arg)) { /* decimal values and 0x... hex values */ | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | while (isxdigit((int)*arg)) { /* hex values not preceded by 0x */ | 
|  | arg++; | 
|  | } | 
|  | if (*arg == ',' || *arg == '\0') { /* end of field or string */ | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0;	/* not a value */ | 
|  | } | 
|  |  | 
|  | /* Expand a list of CPU/memory maps or masks containing multipliers. | 
|  | * For example, change "1*4,2*4" to "1,1,1,1,2,2,2,2" | 
|  | * list IN - input mask or map | 
|  | * type IN - "mask_cpu", "map_mem", used for error messages | 
|  | * error_code - output SLURM_SUCCESS or SLURM_ERROR on failure | 
|  | * RET - output mask or map, value must be xfreed */ | 
|  | static char *_expand_mult(char *list, char *type, int *error_code) | 
|  | { | 
|  | char *ast, *end_ptr = NULL, *result = NULL, *save_ptr = NULL; | 
|  | char *sep = "", *tmp, *tok; | 
|  | int (*isfunc) (int) = isdigit; | 
|  | bool mask = false; | 
|  |  | 
|  | *error_code = SLURM_SUCCESS; | 
|  |  | 
|  | if (!list)		/* Nothing to convert */ | 
|  | return NULL; | 
|  |  | 
|  | tmp = xstrdup(list); | 
|  |  | 
|  | if (!xstrncmp(type, "mask", 4)) { | 
|  | isfunc = isxdigit; | 
|  | mask = true; | 
|  | } | 
|  |  | 
|  | tok = strtok_r(tmp, ",", &save_ptr); | 
|  | while (tok) { | 
|  | long int i; | 
|  | long int count = 1; | 
|  | if (mask && !xstrncmp(tok, "0x", 2)) | 
|  | tok+=2; | 
|  |  | 
|  | ast = strchr(tok, '*'); | 
|  | if (ast) { | 
|  | /* Starting from 1 since we know that ast[0] == '*' */ | 
|  | for (int i = 1; ast[i]; i++) | 
|  | if (!isdigit(ast[i])) { | 
|  | error("Failed to validate number: %s, the offending character is %c", | 
|  | ast, ast[i]); | 
|  | *error_code = SLURM_ERROR; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | count = strtol(ast + 1, &end_ptr, 10); | 
|  | if ((count <= 0) || (end_ptr[0] != '\0') || | 
|  | (count == LONG_MIN) || (count == LONG_MAX)) { | 
|  | error("Invalid %s multiplier: %s", | 
|  | type, ast + 1); | 
|  | xfree(result); | 
|  | *error_code = SLURM_ERROR; | 
|  | break; | 
|  | } | 
|  | ast[0] = '\0'; | 
|  | } | 
|  |  | 
|  | for (i = 0; tok[i]; i++) { | 
|  | if (!isfunc(tok[i])) { | 
|  | error("Failed to validate number: %s, the offending character is %c", | 
|  | tok, tok[i]); | 
|  | *error_code = SLURM_ERROR; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | xstrfmtcat(result, "%s%s", sep, tok); | 
|  | sep = ","; | 
|  | } | 
|  | tok = strtok_r(NULL, ",", &save_ptr); | 
|  | } | 
|  | xfree(tmp); | 
|  |  | 
|  | if (!result) { | 
|  | error("Failed to expand list: '%s'", list); | 
|  | *error_code = SLURM_ERROR; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static bool _have_task_affinity(void) | 
|  | { | 
|  | if (!xstrcmp(slurm_conf.task_plugin, "task/none")) | 
|  | return false; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * slurm_sprint_cpu_bind_type | 
|  | * | 
|  | * Given a cpu_bind_type, report all flag settings in str | 
|  | * IN  - cpu_bind_type | 
|  | * OUT - str | 
|  | */ | 
|  | void slurm_sprint_cpu_bind_type(char *str, cpu_bind_type_t cpu_bind_type) | 
|  | { | 
|  | if (!str) | 
|  | return; | 
|  |  | 
|  | str[0] = '\0'; | 
|  |  | 
|  | if (cpu_bind_type & CPU_BIND_VERBOSE) | 
|  | strcat(str, "verbose,"); | 
|  |  | 
|  | if (cpu_bind_type & CPU_BIND_TO_THREADS) | 
|  | strcat(str, "threads,"); | 
|  | if (cpu_bind_type & CPU_BIND_TO_CORES) | 
|  | strcat(str, "cores,"); | 
|  | if (cpu_bind_type & CPU_BIND_TO_SOCKETS) | 
|  | strcat(str, "sockets,"); | 
|  | if (cpu_bind_type & CPU_BIND_TO_LDOMS) | 
|  | strcat(str, "ldoms,"); | 
|  | if (cpu_bind_type & CPU_BIND_NONE) | 
|  | strcat(str, "none,"); | 
|  | if (cpu_bind_type & CPU_BIND_MAP) | 
|  | strcat(str, "map_cpu,"); | 
|  | if (cpu_bind_type & CPU_BIND_MASK) | 
|  | strcat(str, "mask_cpu,"); | 
|  | if (cpu_bind_type & CPU_BIND_LDRANK) | 
|  | strcat(str, "rank_ldom,"); | 
|  | if (cpu_bind_type & CPU_BIND_LDMAP) | 
|  | strcat(str, "map_ldom,"); | 
|  | if (cpu_bind_type & CPU_BIND_LDMASK) | 
|  | strcat(str, "mask_ldom,"); | 
|  | if (cpu_bind_type & CPU_BIND_ONE_THREAD_PER_CORE) | 
|  | strcat(str, "one_thread,"); | 
|  |  | 
|  | if (cpu_bind_type & CPU_AUTO_BIND_TO_THREADS) | 
|  | strcat(str, "autobind=threads,"); | 
|  | if (cpu_bind_type & CPU_AUTO_BIND_TO_CORES) | 
|  | strcat(str, "autobind=cores,"); | 
|  | if (cpu_bind_type & CPU_AUTO_BIND_TO_SOCKETS) | 
|  | strcat(str, "autobind=sockets,"); | 
|  |  | 
|  | if (cpu_bind_type & CPU_BIND_OFF) | 
|  | strcat(str, "off,"); | 
|  |  | 
|  | if (*str) { | 
|  | str[strlen(str)-1] = '\0';	/* remove trailing ',' */ | 
|  | } else { | 
|  | strcat(str, "(null type)");	/* no bits set */ | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * slurm_xstr_mem_bind_type | 
|  | * | 
|  | * Given a mem_bind_type, report all flag settings in str | 
|  | * IN  - mem_bind_type | 
|  | * OUT - str | 
|  | */ | 
|  | extern char *slurm_xstr_mem_bind_type(mem_bind_type_t mem_bind_type) | 
|  | { | 
|  | char *str = NULL; | 
|  |  | 
|  | if (mem_bind_type & MEM_BIND_VERBOSE) | 
|  | xstrcat(str, "verbose,"); | 
|  | if (mem_bind_type & MEM_BIND_PREFER) | 
|  | xstrcat(str, "prefer,"); | 
|  | if (mem_bind_type & MEM_BIND_SORT) | 
|  | xstrcat(str, "sort,"); | 
|  | if (mem_bind_type & MEM_BIND_NONE) | 
|  | xstrcat(str, "none,"); | 
|  | if (mem_bind_type & MEM_BIND_RANK) | 
|  | xstrcat(str, "rank,"); | 
|  | if (mem_bind_type & MEM_BIND_LOCAL) | 
|  | xstrcat(str, "local,"); | 
|  | if (mem_bind_type & MEM_BIND_MAP) | 
|  | xstrcat(str, "map_mem,"); | 
|  | if (mem_bind_type & MEM_BIND_MASK) | 
|  | xstrcat(str, "mask_mem,"); | 
|  |  | 
|  | if (str) { | 
|  | str[strlen(str)-1] = '\0';	/* remove trailing ',' */ | 
|  | } | 
|  |  | 
|  | return str; | 
|  | } | 
|  |  | 
|  | void slurm_print_cpu_bind_help(void) | 
|  | { | 
|  | if (!_have_task_affinity()) { | 
|  | printf("CPU bind options not supported with current " | 
|  | "configuration\n"); | 
|  | } else { | 
|  | printf( | 
|  | "CPU bind options:\n" | 
|  | "    --cpu-bind=         Bind tasks to CPUs\n" | 
|  | "        q[uiet]         quietly bind before task runs (default)\n" | 
|  | "        v[erbose]       verbosely report binding before task runs\n" | 
|  | "        no[ne]          don't bind tasks to CPUs (default)\n" | 
|  | "        map_cpu:<list>  specify a CPU ID binding for each task\n" | 
|  | "                        where <list> is <cpuid1>,<cpuid2>,...<cpuidN>\n" | 
|  | "        mask_cpu:<list> specify a CPU ID binding mask for each task\n" | 
|  | "                        where <list> is <mask1>,<mask2>,...<maskN>\n" | 
|  | "        rank_ldom       bind task by rank to CPUs in a NUMA locality domain\n" | 
|  | "        map_ldom:<list> specify a NUMA locality domain ID for each task\n" | 
|  | "                        where <list> is <ldom1>,<ldom2>,...<ldomN>\n" | 
|  | "        mask_ldom:<list>specify a NUMA locality domain ID mask for each task\n" | 
|  | "                        where <list> is <mask1>,<mask2>,...<maskN>\n" | 
|  | "        sockets         auto-generated masks bind to sockets\n" | 
|  | "        cores           auto-generated masks bind to cores\n" | 
|  | "        threads         auto-generated masks bind to threads\n" | 
|  | "        ldoms           auto-generated masks bind to NUMA locality domains\n" | 
|  | "        help            show this help message\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * verify cpu_bind arguments | 
|  | * | 
|  | * we support different launch policy names | 
|  | * we also allow a verbose setting to be specified | 
|  | *     --cpu-bind=threads | 
|  | *     --cpu-bind=cores | 
|  | *     --cpu-bind=sockets | 
|  | *     --cpu-bind=v | 
|  | *     --cpu-bind=rank,v | 
|  | *     --cpu-bind=rank | 
|  | *     --cpu-bind={MAP_CPU|MASK_CPU}:0,1,2,3,4 | 
|  | * | 
|  | * arg IN - user task binding option | 
|  | * cpu_bind OUT - task binding string | 
|  | * flags OUT OUT - task binding flags | 
|  | * RET SLURM_SUCCESS, SLURM_ERROR (-1) on failure, 1 for return for "help" arg | 
|  | */ | 
|  | extern int slurm_verify_cpu_bind(const char *arg, char **cpu_bind, | 
|  | cpu_bind_type_t *flags) | 
|  | { | 
|  | char *buf, *p, *tok; | 
|  | int bind_bits, bind_to_bits; | 
|  | bool have_binding = _have_task_affinity(); | 
|  | bool log_binding = true; | 
|  | int rc = SLURM_SUCCESS; | 
|  |  | 
|  | bind_bits = CPU_BIND_NONE | CPU_BIND_MAP | CPU_BIND_MASK | | 
|  | CPU_BIND_LDRANK | CPU_BIND_LDMAP | CPU_BIND_LDMASK; | 
|  | bind_to_bits = CPU_BIND_TO_SOCKETS | CPU_BIND_TO_CORES | | 
|  | CPU_BIND_TO_THREADS | CPU_BIND_TO_LDOMS; | 
|  |  | 
|  | buf = xstrdup(arg); | 
|  | p = buf; | 
|  | /* change all ',' delimiters not followed by a digit to ';'  */ | 
|  | /* simplifies parsing tokens while keeping map/mask together */ | 
|  | while (p[0] != '\0') { | 
|  | if ((p[0] == ',') && (!_isvalue(&(p[1])))) | 
|  | p[0] = ';'; | 
|  | p++; | 
|  | } | 
|  |  | 
|  | p = buf; | 
|  | while ((rc == SLURM_SUCCESS) && (tok = strsep(&p, ";"))) { | 
|  | if (xstrcasecmp(tok, "help") == 0) { | 
|  | slurm_print_cpu_bind_help(); | 
|  | xfree(buf); | 
|  | return 1; | 
|  | } | 
|  | if (!have_binding && log_binding) { | 
|  | info("cluster configuration lacks support for cpu binding"); | 
|  | log_binding = false; | 
|  | } | 
|  | if ((xstrcasecmp(tok, "q") == 0) || | 
|  | (xstrcasecmp(tok, "quiet") == 0)) { | 
|  | *flags &= ~CPU_BIND_VERBOSE; | 
|  | } else if ((xstrcasecmp(tok, "v") == 0) || | 
|  | (xstrcasecmp(tok, "verbose") == 0)) { | 
|  | *flags |= CPU_BIND_VERBOSE; | 
|  | } else if ((xstrcasecmp(tok, "one_thread") == 0)) { | 
|  | *flags |= CPU_BIND_ONE_THREAD_PER_CORE; | 
|  | } else if ((xstrcasecmp(tok, "no") == 0) || | 
|  | (xstrcasecmp(tok, "none") == 0)) { | 
|  | _clear_then_set((int *)flags, bind_bits, CPU_BIND_NONE); | 
|  | xfree(*cpu_bind); | 
|  | } else if (xstrcasecmp(tok, "rank") == 0) { | 
|  | info("Ignoring --cpu-bind=rank. Rank binding is obsolete."); | 
|  | xfree(*cpu_bind); | 
|  | } else if ((xstrncasecmp(tok, "map_cpu", 7) == 0) || | 
|  | (xstrncasecmp(tok, "mapcpu", 6) == 0)) { | 
|  | char *list; | 
|  | (void) strsep(&tok, ":="); | 
|  | list = strsep(&tok, ":=");  /* THIS IS NOT REDUNDANT */ | 
|  | _clear_then_set((int *)flags, bind_bits, CPU_BIND_MAP); | 
|  | xfree(*cpu_bind); | 
|  | if (list && *list) { | 
|  | *cpu_bind = _expand_mult(list, "map_cpu", &rc); | 
|  | } else { | 
|  | error("missing list for \"--cpu-bind=map_cpu:<list>\""); | 
|  | rc = SLURM_ERROR; | 
|  | } | 
|  | } else if ((xstrncasecmp(tok, "mask_cpu", 8) == 0) || | 
|  | (xstrncasecmp(tok, "maskcpu", 7) == 0)) { | 
|  | char *list; | 
|  | (void) strsep(&tok, ":="); | 
|  | list = strsep(&tok, ":=");  /* THIS IS NOT REDUNDANT */ | 
|  | _clear_then_set((int *)flags, bind_bits, CPU_BIND_MASK); | 
|  | xfree(*cpu_bind); | 
|  | if (list && *list) { | 
|  | *cpu_bind = _expand_mult(list, "mask_cpu", &rc); | 
|  | } else { | 
|  | error("missing list for \"--cpu-bind=mask_cpu:<list>\""); | 
|  | rc = SLURM_ERROR; | 
|  | } | 
|  | } else if (xstrcasecmp(tok, "rank_ldom") == 0) { | 
|  | _clear_then_set((int *)flags, bind_bits, | 
|  | CPU_BIND_LDRANK); | 
|  | xfree(*cpu_bind); | 
|  | } else if ((xstrncasecmp(tok, "map_ldom", 8) == 0) || | 
|  | (xstrncasecmp(tok, "mapldom", 7) == 0)) { | 
|  | char *list; | 
|  | (void) strsep(&tok, ":="); | 
|  | list = strsep(&tok, ":=");  /* THIS IS NOT REDUNDANT */ | 
|  | _clear_then_set((int *)flags, bind_bits, | 
|  | CPU_BIND_LDMAP); | 
|  | xfree(*cpu_bind); | 
|  | if (list && *list) { | 
|  | *cpu_bind = _expand_mult(list, "map_ldom", &rc); | 
|  | } else { | 
|  | error("missing list for \"--cpu-bind=map_ldom:<list>\""); | 
|  | rc = SLURM_ERROR; | 
|  | } | 
|  | } else if ((xstrncasecmp(tok, "mask_ldom", 9) == 0) || | 
|  | (xstrncasecmp(tok, "maskldom", 8) == 0)) { | 
|  | char *list; | 
|  | (void) strsep(&tok, ":="); | 
|  | list = strsep(&tok, ":=");  /* THIS IS NOT REDUNDANT */ | 
|  | _clear_then_set((int *)flags, bind_bits, | 
|  | CPU_BIND_LDMASK); | 
|  | xfree(*cpu_bind); | 
|  | if (list && *list) { | 
|  | *cpu_bind = _expand_mult(list, "mask_ldom",&rc); | 
|  | } else { | 
|  | error("missing list for \"--cpu-bind=" | 
|  | "mask_ldom:<list>\""); | 
|  | rc = SLURM_ERROR; | 
|  | } | 
|  | } else if ((xstrcasecmp(tok, "socket") == 0) || | 
|  | (xstrcasecmp(tok, "sockets") == 0)) { | 
|  | _clear_then_set((int *)flags, bind_to_bits, | 
|  | CPU_BIND_TO_SOCKETS); | 
|  | } else if ((xstrcasecmp(tok, "core") == 0) || | 
|  | (xstrcasecmp(tok, "cores") == 0)) { | 
|  | _clear_then_set((int *)flags, bind_to_bits, | 
|  | CPU_BIND_TO_CORES); | 
|  | } else if ((xstrcasecmp(tok, "thread") == 0) || | 
|  | (xstrcasecmp(tok, "threads") == 0)) { | 
|  | _clear_then_set((int *)flags, bind_to_bits, | 
|  | CPU_BIND_TO_THREADS); | 
|  | } else if ((xstrcasecmp(tok, "ldom") == 0) || | 
|  | (xstrcasecmp(tok, "ldoms") == 0)) { | 
|  | _clear_then_set((int *)flags, bind_to_bits, | 
|  | CPU_BIND_TO_LDOMS); | 
|  | } else { | 
|  | error("unrecognized --cpu-bind argument \"%s\"", tok); | 
|  | rc = SLURM_ERROR; | 
|  | } | 
|  | } | 
|  | xfree(buf); | 
|  | if (rc) | 
|  | fatal("Failed to parse --cpu-bind= values."); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Translate a CPU bind string to its equivalent numeric value | 
|  | * cpu_bind_str IN - string to translate | 
|  | * flags OUT - equlvalent numeric value | 
|  | * RET SLURM_SUCCESS or SLURM_ERROR | 
|  | */ | 
|  | extern int xlate_cpu_bind_str(char *cpu_bind_str, uint32_t *flags) | 
|  | { | 
|  | int rc = SLURM_SUCCESS; | 
|  | char *save_ptr = NULL, *tok, *tmp; | 
|  | bool have_bind_type = false; | 
|  |  | 
|  | *flags = 0; | 
|  | if (!cpu_bind_str) | 
|  | return rc; | 
|  |  | 
|  | tmp = xstrdup(cpu_bind_str); | 
|  | tok = strtok_r(tmp, ",;", &save_ptr); | 
|  | while (tok) { | 
|  | if ((xstrcasecmp(tok, "no") == 0) || | 
|  | (xstrcasecmp(tok, "none") == 0)) { | 
|  | if (have_bind_type) { | 
|  | rc = SLURM_ERROR; | 
|  | break; | 
|  | } else { | 
|  | *flags |= CPU_BIND_NONE; | 
|  | have_bind_type = true; | 
|  | } | 
|  | } else if ((xstrcasecmp(tok, "socket") == 0) || | 
|  | (xstrcasecmp(tok, "sockets") == 0)) { | 
|  | if (have_bind_type) { | 
|  | rc = SLURM_ERROR; | 
|  | break; | 
|  | } else { | 
|  | *flags |= CPU_BIND_TO_SOCKETS; | 
|  | have_bind_type = true; | 
|  | } | 
|  | } else if ((xstrcasecmp(tok, "ldom") == 0) || | 
|  | (xstrcasecmp(tok, "ldoms") == 0)) { | 
|  | if (have_bind_type) { | 
|  | rc = SLURM_ERROR; | 
|  | break; | 
|  | } else { | 
|  | *flags |= CPU_BIND_TO_LDOMS; | 
|  | have_bind_type = true; | 
|  | } | 
|  | } else if ((xstrcasecmp(tok, "core") == 0) || | 
|  | (xstrcasecmp(tok, "cores") == 0)) { | 
|  | if (have_bind_type) { | 
|  | rc = SLURM_ERROR; | 
|  | break; | 
|  | } else { | 
|  | *flags |= CPU_BIND_TO_CORES; | 
|  | have_bind_type = true; | 
|  | } | 
|  | } else if ((xstrcasecmp(tok, "thread") == 0) || | 
|  | (xstrcasecmp(tok, "threads") == 0)) { | 
|  | if (have_bind_type) { | 
|  | rc = SLURM_ERROR; | 
|  | break; | 
|  | } else { | 
|  | *flags |= CPU_BIND_TO_THREADS; | 
|  | have_bind_type = true; | 
|  | } | 
|  | } else if (xstrcasecmp(tok, "off") == 0) { | 
|  | if (have_bind_type) { | 
|  | rc = SLURM_ERROR; | 
|  | break; | 
|  | } else { | 
|  | *flags |= CPU_BIND_OFF; | 
|  | have_bind_type = true; | 
|  | } | 
|  | } else if ((xstrcasecmp(tok, "v") == 0) || | 
|  | (xstrcasecmp(tok, "verbose") == 0)) { | 
|  | *flags |= CPU_BIND_VERBOSE; | 
|  | } else { | 
|  | /* Other options probably not desirable to support */ | 
|  | rc = SLURM_ERROR; | 
|  | break; | 
|  | } | 
|  | tok = strtok_r(NULL, ",;", &save_ptr); | 
|  | } | 
|  | xfree(tmp); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | void slurm_print_mem_bind_help(void) | 
|  | { | 
|  | printf( | 
|  | "Memory bind options:\n" | 
|  | "    --mem-bind=         Bind memory to locality domains (ldom)\n" | 
|  | "        nosort          avoid sorting pages at startup\n" | 
|  | "        sort            sort pages at startup\n" | 
|  | "        q[uiet]         quietly bind before task runs (default)\n" | 
|  | "        v[erbose]       verbosely report binding before task runs\n" | 
|  | "        no[ne]          don't bind tasks to memory (default)\n" | 
|  | "        rank            bind by task rank\n" | 
|  | "        local           bind to memory local to processor\n" | 
|  | "        map_mem:<list>  specify a memory binding for each task\n" | 
|  | "                        where <list> is <cpuid1>,<cpuid2>,...<cpuidN>\n" | 
|  | "        mask_mem:<list> specify a memory binding mask for each tasks\n" | 
|  | "                        where <list> is <mask1>,<mask2>,...<maskN>\n" | 
|  | "        help            show this help message\n"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * verify mem_bind arguments | 
|  | * | 
|  | * we support different memory binding names | 
|  | * we also allow a verbose setting to be specified | 
|  | *     --mem-bind=v | 
|  | *     --mem-bind=rank,v | 
|  | *     --mem-bind=rank | 
|  | *     --mem-bind={MAP_MEM|MASK_MEM}:0,1,2,3,4 | 
|  | * | 
|  | * returns -1 on error, 0 otherwise | 
|  | */ | 
|  | int slurm_verify_mem_bind(const char *arg, char **mem_bind, | 
|  | mem_bind_type_t *flags) | 
|  | { | 
|  | char *buf, *p, *tok; | 
|  | int bind_bits = MEM_BIND_NONE|MEM_BIND_RANK|MEM_BIND_LOCAL| | 
|  | MEM_BIND_MAP|MEM_BIND_MASK; | 
|  | int rc = SLURM_SUCCESS; | 
|  |  | 
|  | if (arg == NULL) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | buf = xstrdup(arg); | 
|  | p = buf; | 
|  | /* change all ',' delimiters not followed by a digit to ';'  */ | 
|  | /* simplifies parsing tokens while keeping map/mask together */ | 
|  | while (p[0] != '\0') { | 
|  | if ((p[0] == ',') && (!_isvalue(&(p[1])))) | 
|  | p[0] = ';'; | 
|  | p++; | 
|  | } | 
|  |  | 
|  | p = buf; | 
|  | while ((rc == SLURM_SUCCESS) && (tok = strsep(&p, ";"))) { | 
|  | if (xstrcasecmp(tok, "help") == 0) { | 
|  | slurm_print_mem_bind_help(); | 
|  | xfree(buf); | 
|  | return 1; | 
|  | } else if ((xstrcasecmp(tok, "p") == 0) || | 
|  | (xstrcasecmp(tok, "prefer") == 0)) { | 
|  | *flags |= MEM_BIND_PREFER; | 
|  | } else if (!xstrcasecmp(tok, "nosort")) { | 
|  | *flags &= ~MEM_BIND_SORT; | 
|  | } else if (!xstrcasecmp(tok, "sort")) { | 
|  | *flags |= MEM_BIND_SORT; | 
|  | } else if ((xstrcasecmp(tok, "q") == 0) || | 
|  | (xstrcasecmp(tok, "quiet") == 0)) { | 
|  | *flags &= ~MEM_BIND_VERBOSE; | 
|  | } else if ((xstrcasecmp(tok, "v") == 0) || | 
|  | (xstrcasecmp(tok, "verbose") == 0)) { | 
|  | *flags |= MEM_BIND_VERBOSE; | 
|  | } else if ((xstrcasecmp(tok, "no") == 0) || | 
|  | (xstrcasecmp(tok, "none") == 0)) { | 
|  | _clear_then_set((int *)flags, bind_bits, MEM_BIND_NONE); | 
|  | xfree(*mem_bind); | 
|  | } else if (xstrcasecmp(tok, "rank") == 0) { | 
|  | _clear_then_set((int *)flags, bind_bits, MEM_BIND_RANK); | 
|  | xfree(*mem_bind); | 
|  | } else if (xstrcasecmp(tok, "local") == 0) { | 
|  | _clear_then_set((int *)flags, bind_bits,MEM_BIND_LOCAL); | 
|  | xfree(*mem_bind); | 
|  | } else if ((xstrncasecmp(tok, "map_mem", 7) == 0) || | 
|  | (xstrncasecmp(tok, "mapmem", 6) == 0)) { | 
|  | char *list; | 
|  | (void) strsep(&tok, ":="); | 
|  | list = strsep(&tok, ":=");  /* THIS IS NOT REDUNDANT */ | 
|  | _clear_then_set((int *)flags, bind_bits, MEM_BIND_MAP); | 
|  | xfree(*mem_bind); | 
|  | if (list && *list) { | 
|  | *mem_bind = _expand_mult(list, "map_mem", &rc); | 
|  | } else { | 
|  | error("missing list for \"--mem-bind=map_mem:<list>\""); | 
|  | rc = SLURM_ERROR; | 
|  | } | 
|  | } else if ((xstrncasecmp(tok, "mask_mem", 8) == 0) || | 
|  | (xstrncasecmp(tok, "maskmem", 7) == 0)) { | 
|  | char *list; | 
|  | (void) strsep(&tok, ":="); | 
|  | list = strsep(&tok, ":=");  /* THIS IS NOT REDUNDANT */ | 
|  | _clear_then_set((int *)flags, bind_bits, MEM_BIND_MASK); | 
|  | xfree(*mem_bind); | 
|  | if (list && *list) { | 
|  | *mem_bind = _expand_mult(list, "mask_mem", &rc); | 
|  | } else { | 
|  | error("missing list for \"--mem-bind=mask_mem:<list>\""); | 
|  | rc = SLURM_ERROR; | 
|  | } | 
|  | } else { | 
|  | error("unrecognized --mem-bind argument \"%s\"", tok); | 
|  | rc = SLURM_ERROR; | 
|  | } | 
|  | } | 
|  |  | 
|  | xfree(buf); | 
|  | return rc; | 
|  | } |