From 1334485a8d97a7fa19681538f8d3272768ea186d Mon Sep 17 00:00:00 2001 From: Moe Jette <jette1@llnl.gov> Date: Tue, 11 Sep 2007 17:17:24 +0000 Subject: [PATCH] Add multi-core options to salloc and sbatch commands (sbatch.patch, from Chris Holmes, HP). --- NEWS | 2 + doc/man/man1/salloc.1 | 125 +++++++- doc/man/man1/sbatch.1 | 126 +++++++- src/common/Makefile.am | 3 +- src/common/Makefile.in | 9 +- src/common/proc_args.c | 651 +++++++++++++++++++++++++++++++++++++++++ src/common/proc_args.h | 106 +++++++ src/salloc/opt.c | 568 +++++++++++++++++------------------ src/salloc/opt.h | 15 + src/salloc/salloc.c | 28 ++ src/sbatch/opt.c | 629 +++++++++++++++++---------------------- src/sbatch/opt.h | 19 +- src/sbatch/sbatch.c | 27 +- src/srun/opt.c | 632 +++------------------------------------ 14 files changed, 1671 insertions(+), 1269 deletions(-) create mode 100644 src/common/proc_args.c create mode 100644 src/common/proc_args.h diff --git a/NEWS b/NEWS index 94428db409d..f2d813c233d 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ documents those changes that are of interest to users and admins. -- Add support for feature count in job contraints, for example srun --nodes=16 --constraint=graphics*4 ... Based upon work by Kumar Krishna (HP, India). + -- Add multi-core options to salloc and sbatch commands (sbatch.patch, + from Chris Holmes, HP). * Changes in SLURM 1.3.0-pre2 ============================= diff --git a/doc/man/man1/salloc.1 b/doc/man/man1/salloc.1 index 554d668750d..45da60c0270 100644 --- a/doc/man/man1/salloc.1 +++ b/doc/man/man1/salloc.1 @@ -13,6 +13,34 @@ The command may be any program the user wishes. Some typical commands are xterm .SH "OPTIONS" .LP +.TP +\fB\-B\fR \fB\-\-extra\-node\-info\fR=\fIsockets\fR[:\fIcores\fR[:\fIthreads\fR]] +Request a specific allocation of resources with details as to the +number and type of computational resources within a cluster: +number of sockets (or physical processors) per node, +cores per socket, and threads per core. +The total amount of resources being requested is the product of all of +the terms. +As with \-\-nodes, each value can be a single number or a range (e.g. min\-max). +An asterisk (*) can be used as a placeholder indicating that all available +resources of that type are to be utilized. +As with nodes, the individual levels can also be specified in separate +options if desired: +.nf + \fB\-\-sockets\-per\-node\fR=\fIsockets\fR + \fB\-\-cores\-per\-socket\fR=\fIcores\fR + \fB\-\-threads\-per\-core\fR=\fIthreads\fR +.fi +When the task/affinity plugin is enabled, +specifying an allocation in this manner also instructs SLURM to use +a CPU affinity mask to guarantee the request is filled as specified. +NOTE: Support for these options are configuration dependent. +The task/affinity plugin must be configured. +In addition either select/linear or select/cons_res plugin must be +configured. +If select/cons_res is configured, it must have a parameter of CR_Core, +CR_Core_Memory, CR_Socket, or CR_Socket_Memory. + .TP \fB\-\-begin\fR[=]<\fItime\fR> Submit the batch script to the SLURM controller immediately, like normal, but @@ -121,6 +149,26 @@ may be the group name or the numerical group ID. \fB\-h\fR, \fB\-\-help\fR Display help information and exit. +.TP +\fB\-\-hint\fR=\fItype\fR +Bind tasks according to application hints +.RS +.TP +.B compute_bound +Select settings for compute bound applications: +use all cores in each physical CPU +.TP +.B memory_bound +Select settings for memory bound applications: +use only one core in each physical CPU +.TP +.B [no]multithread +[don't] use extra threads with in-core multi-threading +which can benefit communication intensive applications +.B help +show this help message +.RE + .TP \fB\-I\fR,\fB\-\-immediate\fR Grab the requested resources immediately, or abort if the resources are not @@ -161,6 +209,47 @@ new job steps on the remaining nodes in their allocation. By default SLURM terminates the entire job allocation if any node fails in its range of allocated nodes. +.TP +\fB\-m\fR, \fB\-\-distribution\fR= +(\fIblock\fR|\fIcyclic\fR|\fIarbitrary\fR|\fIplane=<options>\fR) +Specify an alternate distribution method for remote processes. +.RS +.TP +.B block +The block method of distribution will allocate processes in\-order to +the cpus on a node. If the number of processes exceeds the number of +cpus on all of the nodes in the allocation then all nodes will be +utilized. For example, consider an allocation of three nodes each with +two cpus. A four\-process block distribution request will distribute +those processes to the nodes with processes one and two on the first +node, process three on the second node, and process four on the third node. +Block distribution is the default behavior if the number of tasks +exceeds the number of nodes requested. +.TP +.B cyclic +The cyclic method distributes processes in a round\-robin fashion across +the allocated nodes. That is, process one will be allocated to the first +node, process two to the second, and so on. This is the default behavior +if the number of tasks is no larger than the number of nodes requested. +.TP +.B plane +The tasks are distributed in blocks of a specified size. +The options include a number representing the size of the task block. +This is followed by an optional specification of the task distribution +scheme within a block of tasks and between the blocks of tasks. +For more details (including examples and diagrams), please see +http://www.llnl.gov/linux/slurm/mc_support.html and +http://www.llnl.gov/linux/slurm/dist_plane.html. +.TP +.B arbitrary +The arbitrary method of distribution will allocate processes in\-order as +listed in file designated by the environment variable SLURM_HOSTFILE. If +this variable is listed it will over ride any other method specified. +If not set the method will default to block. Inside the hostfile must +contain at minimum the number of hosts requested. If requesting tasks +(-n) your tasks will be laid out on the nodes in the order of the file. +.RE + .TP \fB\-\-mail\-type\fR=\fItype\fR Notify user by email when certain event types occur. @@ -208,7 +297,7 @@ The job will be allocated as many nodes as possible within the range specified and without delaying the initiation of the job. .TP -\fB\-n\fR, \fB\-\-tasks\fR[=]<\fInumber\fR> +\fB\-n\fR, \fB\-\-ntasks\fR[=]<\fInumber\fR> salloc does not launch tasks, it requests an allocation of resources and submits a batch script. However this \-\-tasks option advizes the SLURM controller that job steps run within this allocation will launch a maximum of \fInumber\fR @@ -224,6 +313,40 @@ is from \-10000 (highest priority) to 10000 (lowest priority). Only privileged users can specify a negative adjustment. NOTE: This option is presently ignored if SchedulerType=sched/maui. +.TP +\fB\-\-ntasks\-per\-core\fR=\fIntasks\fR +Request that no more than \fIntasks\fR be invoked on each core. +Similar to \fB\-\-ntasks\-per\-node\fR except at the core level +instead of the node level. Masks will automatically be generated +to bind the tasks to specific core unless \fB\-\-cpu_bind=none\fR +is specified. +NOTE: This option is not supported unless \fISelectType=CR_Core\fR +or \fISelectType=CR_Core_Memory\fR is configured. + +.TP +\fB\-\-ntasks\-per\-socket\fR=\fIntasks\fR +Request that no more than \fIntasks\fR be invoked on each socket. +Similar to \fB\-\-ntasks\-per\-node\fR except at the socket level +instead of the node level. Masks will automatically be generated +to bind the tasks to specific sockets unless \fB\-\-cpu_bind=none\fR +is specified. +NOTE: This option is not supported unless \fISelectType=CR_Socket\fR +or \fISelectType=CR_Socket_Memory\fR is configured. + +.TP +\fB\-\-ntasks\-per\-node\fR=\fIntasks\fR +Request that no more than \fIntasks\fR be invoked on each node. +This is similiar to using \fB\-\-cpus\-per\-task\fR=\fIncpus\fR +but does not require knowledge of the actual number of cpus on +each node. In some cases, it is more convenient to be able to +request that no more than a specific number of ntasks be invoked +on each node. Examples of this include submitting +a hybrid MPI/OpenMP app where only one MPI "task/rank" should be +assigned to each node while allowing the OpenMP portion to utilize +all of the parallelism present in the node, or submitting a single +setup/cleanup/monitoring job to each node of a pre\-existing +allocation as one step in a larger job script. + .TP \fB\-\-no\-bell\fR Silence salloc's use of the terminal bell. Also see the option \fB\-\-bell\fR. diff --git a/doc/man/man1/sbatch.1 b/doc/man/man1/sbatch.1 index c156baa88cc..78e10250248 100644 --- a/doc/man/man1/sbatch.1 +++ b/doc/man/man1/sbatch.1 @@ -23,6 +23,34 @@ allocated nodes. .SH "OPTIONS" .LP +.TP +\fB\-B\fR \fB\-\-extra\-node\-info\fR=\fIsockets\fR[:\fIcores\fR[:\fIthreads\fR]] +Request a specific allocation of resources with details as to the +number and type of computational resources within a cluster: +number of sockets (or physical processors) per node, +cores per socket, and threads per core. +The total amount of resources being requested is the product of all of +the terms. +As with \-\-nodes, each value can be a single number or a range (e.g. min\-max). +An asterisk (*) can be used as a placeholder indicating that all available +resources of that type are to be utilized. +As with nodes, the individual levels can also be specified in separate +options if desired: +.nf + \fB\-\-sockets\-per\-node\fR=\fIsockets\fR + \fB\-\-cores\-per\-socket\fR=\fIcores\fR + \fB\-\-threads\-per\-core\fR=\fIthreads\fR +.fi +When the task/affinity plugin is enabled, +specifying an allocation in this manner also instructs SLURM to use +a CPU affinity mask to guarantee the request is filled as specified. +NOTE: Support for these options are configuration dependent. +The task/affinity plugin must be configured. +In addition either select/linear or select/cons_res plugin must be +configured. +If select/cons_res is configured, it must have a parameter of CR_Core, +CR_Core_Memory, CR_Socket, or CR_Socket_Memory. + .TP \fB\-\-begin\fR[=]<\fItime\fR> Submit the batch script to the SLURM controller immediately, like normal, but @@ -146,6 +174,26 @@ may be the group name or the numerical group ID. \fB\-h\fR, \fB\-\-help\fR Display help information and exit. +.TP +\fB\-\-hint\fR=\fItype\fR +Bind tasks according to application hints +.RS +.TP +.B compute_bound +Select settings for compute bound applications: +use all cores in each physical CPU +.TP +.B memory_bound +Select settings for memory bound applications: +use only one core in each physical CPU +.TP +.B [no]multithread +[don't] use extra threads with in-core multi-threading +which can benefit communication intensive applications +.B help +show this help message +.RE + .TP \fB\-I\fR,\fB\-\-immediate\fR The batch script will only be submitted to the controller if the resources @@ -202,6 +250,47 @@ new job steps on the remaining nodes in their allocation. By default SLURM terminates the entire job allocation if any node fails in its range of allocated nodes. +.TP +\fB\-m\fR, \fB\-\-distribution\fR= +(\fIblock\fR|\fIcyclic\fR|\fIarbitrary\fR|\fIplane=<options>\fR) +Specify an alternate distribution method for remote processes. +.RS +.TP +.B block +The block method of distribution will allocate processes in\-order to +the cpus on a node. If the number of processes exceeds the number of +cpus on all of the nodes in the allocation then all nodes will be +utilized. For example, consider an allocation of three nodes each with +two cpus. A four\-process block distribution request will distribute +those processes to the nodes with processes one and two on the first +node, process three on the second node, and process four on the third node. +Block distribution is the default behavior if the number of tasks +exceeds the number of nodes requested. +.TP +.B cyclic +The cyclic method distributes processes in a round\-robin fashion across +the allocated nodes. That is, process one will be allocated to the first +node, process two to the second, and so on. This is the default behavior +if the number of tasks is no larger than the number of nodes requested. +.TP +.B plane +The tasks are distributed in blocks of a specified size. +The options include a number representing the size of the task block. +This is followed by an optional specification of the task distribution +scheme within a block of tasks and between the blocks of tasks. +For more details (including examples and diagrams), please see +http://www.llnl.gov/linux/slurm/mc_support.html and +http://www.llnl.gov/linux/slurm/dist_plane.html. +.TP +.B arbitrary +The arbitrary method of distribution will allocate processes in\-order as +listed in file designated by the environment variable SLURM_HOSTFILE. If +this variable is listed it will over ride any other method specified. +If not set the method will default to block. Inside the hostfile must +contain at minimum the number of hosts requested. If requesting tasks +(-n) your tasks will be laid out on the nodes in the order of the file. +.RE + .TP \fB\-\-mail\-type\fR=\fItype\fR Notify user by email when certain event types occur. @@ -249,7 +338,7 @@ The job will be allocated as many nodes as possible within the range specified and without delaying the initiation of the job. .TP -\fB\-n\fR, \fB\-\-tasks\fR[=]<\fInumber\fR> +\fB\-n\fR, \fB\-\-ntasks\fR[=]<\fInumber\fR> sbatch does not launch tasks, it requests an allocation of resources and submits a batch script. However this \-\-tasks option advizes the SLURM controller that job steps run within this allocation will launch a maximum of \fInumber\fR @@ -273,9 +362,38 @@ to restart the job (for example, after a scheduled downtime). When a job is requeued, the batch script is initiated from its beginning. .TP -\fB\-\-ntasks\-per\-node\fR[=]<\fIn\fR> -Specify the number of tasks to be launched per node. -Equivalent to \fB\-\-tasks\-per\-node\fR. +\fB\-\-ntasks\-per\-core\fR=\fIntasks\fR +Request that no more than \fIntasks\fR be invoked on each core. +Similar to \fB\-\-ntasks\-per\-node\fR except at the core level +instead of the node level. Masks will automatically be generated +to bind the tasks to specific core unless \fB\-\-cpu_bind=none\fR +is specified. +NOTE: This option is not supported unless \fISelectType=CR_Core\fR +or \fISelectType=CR_Core_Memory\fR is configured. + +.TP +\fB\-\-ntasks\-per\-socket\fR=\fIntasks\fR +Request that no more than \fIntasks\fR be invoked on each socket. +Similar to \fB\-\-ntasks\-per\-node\fR except at the socket level +instead of the node level. Masks will automatically be generated +to bind the tasks to specific sockets unless \fB\-\-cpu_bind=none\fR +is specified. +NOTE: This option is not supported unless \fISelectType=CR_Socket\fR +or \fISelectType=CR_Socket_Memory\fR is configured. + +.TP +\fB\-\-ntasks\-per\-node\fR=\fIntasks\fR +Request that no more than \fIntasks\fR be invoked on each node. +This is similiar to using \fB\-\-cpus\-per\-task\fR=\fIncpus\fR +but does not require knowledge of the actual number of cpus on +each node. In some cases, it is more convenient to be able to +request that no more than a specific number of ntasks be invoked +on each node. Examples of this include submitting +a hybrid MPI/OpenMP app where only one MPI "task/rank" should be +assigned to each node while allowing the OpenMP portion to utilize +all of the parallelism present in the node, or submitting a single +setup/cleanup/monitoring job to each node of a pre\-existing +allocation as one step in a larger job script. .TP \fB\-O\fR, \fB\-\-overcommit\fR diff --git a/src/common/Makefile.am b/src/common/Makefile.am index a747eefa2a6..0a56656c7fe 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -81,7 +81,8 @@ libcommon_la_SOURCES = \ global_defaults.c \ timers.c timers.h \ slurm_xlator.h \ - stepd_api.c stepd_api.h + stepd_api.c stepd_api.h \ + proc_args.c proc_args.h EXTRA_libcommon_la_SOURCES = \ $(extra_unsetenv_src) diff --git a/src/common/Makefile.in b/src/common/Makefile.in index 80a4cf55fe5..5f5303b5bea 100644 --- a/src/common/Makefile.in +++ b/src/common/Makefile.in @@ -93,7 +93,8 @@ am__libcommon_la_SOURCES_DIST = xmalloc.c xmalloc.h xassert.c \ hostlist.h slurm_step_layout.c slurm_step_layout.h \ checkpoint.c checkpoint.h parse_time.c parse_time.h \ job_options.c job_options.h global_defaults.c timers.c \ - timers.h slurm_xlator.h stepd_api.c stepd_api.h + timers.h slurm_xlator.h stepd_api.c stepd_api.h proc_args.c \ + proc_args.h @HAVE_UNSETENV_FALSE@am__objects_1 = unsetenv.lo am_libcommon_la_OBJECTS = xmalloc.lo xassert.lo xstring.lo xsignal.lo \ forward.lo strlcpy.lo list.lo net.lo fd.lo log.lo cbuf.lo \ @@ -108,7 +109,7 @@ am_libcommon_la_OBJECTS = xmalloc.lo xassert.lo xstring.lo xsignal.lo \ getopt1.lo $(am__objects_1) slurm_selecttype_info.lo \ slurm_resource_info.lo hostlist.lo slurm_step_layout.lo \ checkpoint.lo parse_time.lo job_options.lo global_defaults.lo \ - timers.lo stepd_api.lo + timers.lo stepd_api.lo proc_args.lo am__EXTRA_libcommon_la_SOURCES_DIST = unsetenv.c unsetenv.h libcommon_la_OBJECTS = $(am_libcommon_la_OBJECTS) libcommon_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -381,7 +382,8 @@ libcommon_la_SOURCES = \ global_defaults.c \ timers.c timers.h \ slurm_xlator.h \ - stepd_api.c stepd_api.h + stepd_api.c stepd_api.h \ + proc_args.c proc_args.h EXTRA_libcommon_la_SOURCES = \ $(extra_unsetenv_src) @@ -487,6 +489,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugrack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugstack.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_args.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read_config.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safeopen.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slurm_auth.Plo@am__quote@ diff --git a/src/common/proc_args.c b/src/common/proc_args.c new file mode 100644 index 00000000000..b6ca8ae3700 --- /dev/null +++ b/src/common/proc_args.c @@ -0,0 +1,651 @@ +/*****************************************************************************\ + * proc_args.c - helper functions for command argument processing + * $Id: opt.h 11996 2007-08-10 20:36:26Z jette $ + ***************************************************************************** + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. + * Written by Christopher Holmes <cholmes@hp.com>, who borrowed heavily + * from existing SLURM source code, particularly src/srun/opt.c + * + * This file is part of SLURM, a resource management program. + * For details, see <http://www.llnl.gov/linux/slurm/>. + * + * 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. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +\*****************************************************************************/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> /* strcpy, strncasecmp */ + +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include <fcntl.h> +#include <stdarg.h> /* va_start */ +#include <stdio.h> +#include <stdlib.h> /* getenv */ +#include <pwd.h> /* getpwuid */ +#include <ctype.h> /* isdigit */ +#include <sys/param.h> /* MAXPATHLEN */ +#include <sys/stat.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> + +#include "src/common/list.h" +#include "src/common/xmalloc.h" +#include "src/common/xstring.h" + +#include "src/common/proc_args.h" + + + + +/* print this version of SLURM */ +void print_slurm_version(void) +{ + printf("%s %s\n", PACKAGE, SLURM_VERSION); +} + +/* + * verify that a distribution type in arg is of a known form + * returns the task_dist_states, or -1 if state is unknown + */ +task_dist_states_t verify_dist_type(const char *arg, uint32_t *plane_size) +{ + int len = strlen(arg); + char *dist_str = NULL; + task_dist_states_t result = SLURM_DIST_UNKNOWN; + bool lllp_dist = false, plane_dist = false; + + dist_str = strchr(arg,':'); + if (dist_str != NULL) { + /* -m cyclic|block:cyclic|block */ + lllp_dist = true; + } else { + /* -m plane=<plane_size> */ + dist_str = strchr(arg,'='); + if(dist_str != NULL) { + *plane_size=atoi(dist_str+1); + len = dist_str-arg; + plane_dist = true; + } + } + + if (lllp_dist) { + if (strcasecmp(arg, "cyclic:cyclic") == 0) { + result = SLURM_DIST_CYCLIC_CYCLIC; + } else if (strcasecmp(arg, "cyclic:block") == 0) { + result = SLURM_DIST_CYCLIC_BLOCK; + } else if (strcasecmp(arg, "block:block") == 0) { + result = SLURM_DIST_BLOCK_BLOCK; + } else if (strcasecmp(arg, "block:cyclic") == 0) { + result = SLURM_DIST_BLOCK_CYCLIC; + } + } else if (plane_dist) { + if (strncasecmp(arg, "plane", len) == 0) { + result = SLURM_DIST_PLANE; + } + } else { + if (strncasecmp(arg, "cyclic", len) == 0) { + result = SLURM_DIST_CYCLIC; + } else if (strncasecmp(arg, "block", len) == 0) { + result = SLURM_DIST_BLOCK; + } else if ((strncasecmp(arg, "arbitrary", len) == 0) || + (strncasecmp(arg, "hostfile", len) == 0)) { + result = SLURM_DIST_ARBITRARY; + } + } + + return result; +} + +/* + * verify that a connection type in arg is of known form + * returns the connection_type or -1 if not recognized + */ +int verify_conn_type(const char *arg) +{ + int len = strlen(arg); + + if (!strncasecmp(arg, "MESH", len)) + return SELECT_MESH; + else if (!strncasecmp(arg, "TORUS", len)) + return SELECT_TORUS; + else if (!strncasecmp(arg, "NAV", len)) + return SELECT_NAV; + + error("invalid --conn-type argument %s ignored.", arg); + return -1; +} + +/* + * verify geometry arguments, must have proper count + * returns -1 on error, 0 otherwise + */ +int verify_geometry(const char *arg, uint16_t *geometry) +{ + char* token, *delimiter = ",x", *next_ptr; + int i, rc = 0; + char* geometry_tmp = xstrdup(arg); + char* original_ptr = geometry_tmp; + + token = strtok_r(geometry_tmp, delimiter, &next_ptr); + for (i=0; i<SYSTEM_DIMENSIONS; i++) { + if (token == NULL) { + error("insufficient dimensions in --geometry"); + rc = -1; + break; + } + geometry[i] = (uint16_t)atoi(token); + if (geometry[i] == 0 || geometry[i] == (uint16_t)NO_VAL) { + error("invalid --geometry argument"); + rc = -1; + break; + } + geometry_tmp = next_ptr; + token = strtok_r(geometry_tmp, delimiter, &next_ptr); + } + if (token != NULL) { + error("too many dimensions in --geometry"); + rc = -1; + } + + if (original_ptr) + xfree(original_ptr); + + return rc; +} + +/* return command name from its full path name */ +char * base_name(char* command) +{ + char *char_ptr, *name; + int i; + + if (command == NULL) + return NULL; + + char_ptr = strrchr(command, (int)'/'); + if (char_ptr == NULL) + char_ptr = command; + else + char_ptr++; + + i = strlen(char_ptr); + name = xmalloc(i+1); + strcpy(name, char_ptr); + return name; +} + +/* + * str_to_bytes(): verify that arg is numeric with optional "G" or "M" at end + * if "G" or "M" is there, multiply by proper power of 2 and return + * number in bytes + */ +long str_to_bytes(const char *arg) +{ + char *buf; + char *endptr; + int end; + int multiplier = 1; + long result; + + buf = xstrdup(arg); + + end = strlen(buf) - 1; + + if (isdigit(buf[end])) { + result = strtol(buf, &endptr, 10); + + if (*endptr != '\0') + result = -result; + + } else { + + switch (toupper(buf[end])) { + + case 'G': + multiplier = 1024; + break; + + case 'M': + /* do nothing */ + break; + + default: + multiplier = -1; + } + + buf[end] = '\0'; + + result = multiplier * strtol(buf, &endptr, 10); + + if (*endptr != '\0') + result = -result; + } + + return result; +} + +/* Convert a string into a node count */ +static int +_str_to_nodes(const char *num_str, char **leftover) +{ + long int num; + char *endptr; + + num = strtol(num_str, &endptr, 10); + if (endptr == num_str) { /* no valid digits */ + *leftover = (char *)num_str; + return 0; + } + if (*endptr != '\0' && (*endptr == 'k' || *endptr == 'K')) { + num *= 1024; + endptr++; + } + *leftover = endptr; + + return (int)num; +} + +/* + * verify that a node count in arg is of a known form (count or min-max) + * OUT min, max specified minimum and maximum node counts + * RET true if valid + */ +bool verify_node_count(const char *arg, int *min_nodes, int *max_nodes) +{ + char *ptr, *min_str, *max_str; + char *leftover; + + /* Does the string contain a "-" character? If so, treat as a range. + * otherwise treat as an absolute node count. */ + if ((ptr = index(arg, '-')) != NULL) { + min_str = xstrndup(arg, ptr-arg); + *min_nodes = _str_to_nodes(min_str, &leftover); + if (!xstring_is_whitespace(leftover)) { + error("\"%s\" is not a valid node count", min_str); + xfree(min_str); + return false; + } + xfree(min_str); + if (*min_nodes == 0) + *min_nodes = 1; + + max_str = xstrndup(ptr+1, strlen(arg)-((ptr+1)-arg)); + *max_nodes = _str_to_nodes(max_str, &leftover); + if (!xstring_is_whitespace(leftover)) { + error("\"%s\" is not a valid node count", max_str); + xfree(max_str); + return false; + } + xfree(max_str); + } else { + *min_nodes = *max_nodes = _str_to_nodes(arg, &leftover); + if (!xstring_is_whitespace(leftover)) { + error("\"%s\" is not a valid node count", arg); + return false; + } + if (*min_nodes == 0) { + /* whitespace does not a valid node count make */ + error("\"%s\" is not a valid node count", arg); + return false; + } + } + + if ((*max_nodes != 0) && (*max_nodes < *min_nodes)) { + error("Maximum node count %d is less than" + " minimum node count %d", + *max_nodes, *min_nodes); + return false; + } + + return true; +} + +/* + * get either 1 or 2 integers for a resource count in the form of either + * (count, min-max, or '*') + * A partial error message is passed in via the 'what' param. + * RET true if valid + */ +bool +get_resource_arg_range(const char *arg, const char *what, int* min, int *max, + bool isFatal) +{ + char *p; + long int result; + + if (*arg == '\0') return true; + + /* wildcard meaning every possible value in range */ + if (*arg == '*' ) { + *min = 1; + *max = INT_MAX; + return true; + } + + result = strtol(arg, &p, 10); + if (*p == 'k' || *p == 'K') { + result *= 1024; + p++; + } + + if (((*p != '\0')&&(*p != '-')) || (result <= 0L)) { + error ("Invalid numeric value \"%s\" for %s.", arg, what); + if (isFatal) exit(1); + return false; + } else if (result > INT_MAX) { + error ("Numeric argument (%ld) to big for %s.", result, what); + if (isFatal) exit(1); + return false; + } + + *min = (int) result; + + if (*p == '\0') return true; + if (*p == '-') p++; + + result = strtol(p, &p, 10); + if (*p == 'k' || *p == 'K') { + result *= 1024; + p++; + } + + if (((*p != '\0')&&(*p != '-')) || (result <= 0L)) { + error ("Invalid numeric value \"%s\" for %s.", arg, what); + if (isFatal) exit(1); + return false; + } else if (result > INT_MAX) { + error ("Numeric argument (%ld) to big for %s.", result, what); + if (isFatal) exit(1); + return false; + } + + *max = (int) result; + + return true; +} + +/* + * verify that a resource counts in arg are of a known form X, X:X, X:X:X, or + * X:X:X:X, where X is defined as either (count, min-max, or '*') + * RET true if valid + */ +bool verify_socket_core_thread_count(const char *arg, + int *min_sockets, int *max_sockets, + int *min_cores, int *max_cores, + int *min_threads, int *max_threads, + cpu_bind_type_t *cpu_bind_type) +{ + bool tmp_val,ret_val; + int i,j; + const char *cur_ptr = arg; + char buf[3][48]; /* each can hold INT64_MAX - INT64_MAX */ + buf[0][0] = '\0'; + buf[1][0] = '\0'; + buf[2][0] = '\0'; + + for (j=0;j<3;j++) { + for (i=0;i<47;i++) { + if (*cur_ptr == '\0' || *cur_ptr ==':') break; + buf[j][i] = *cur_ptr++; + } + if (*cur_ptr == '\0') break; + xassert(*cur_ptr == ':'); + buf[j][i] = '\0'; + cur_ptr++; + } + /* if cpu_bind_type doesn't already have a auto preference, choose + * the level based on the level of the -E specification + */ + if (!(*cpu_bind_type & (CPU_BIND_TO_SOCKETS | + CPU_BIND_TO_CORES | + CPU_BIND_TO_THREADS))) { + if (j == 0) { + *cpu_bind_type |= CPU_BIND_TO_SOCKETS; + } else if (j == 1) { + *cpu_bind_type |= CPU_BIND_TO_CORES; + } else if (j == 2) { + *cpu_bind_type |= CPU_BIND_TO_THREADS; + } + } + buf[j][i] = '\0'; + + ret_val = true; + tmp_val = get_resource_arg_range(&buf[0][0], "first arg of -B", + min_sockets, max_sockets, true); + ret_val = ret_val && tmp_val; + tmp_val = get_resource_arg_range(&buf[1][0], "second arg of -B", + min_cores, max_cores, true); + ret_val = ret_val && tmp_val; + tmp_val = get_resource_arg_range(&buf[2][0], "third arg of -B", + min_threads, max_threads, true); + ret_val = ret_val && tmp_val; + + return ret_val; +} + +/* + * verify that a hint is valid and convert it into the implied settings + * RET true if valid + */ +bool verify_hint(const char *arg, int *min_sockets, int *max_sockets, + int *min_cores, int *max_cores, int *min_threads, + int *max_threads, cpu_bind_type_t *cpu_bind_type) +{ + char *buf, *p, *tok; + if (!arg) { + return true; + } + + 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] == ',') && (!isdigit(p[1]))) + p[0] = ';'; + p++; + } + + p = buf; + while ((tok = strsep(&p, ";"))) { + if (strcasecmp(tok, "help") == 0) { + printf( +"Application hint options:\n" +" --hint= Bind tasks according to application hints\n" +" compute_bound use all cores in each physical CPU\n" +" memory_bound use only one core in each physical CPU\n" +" [no]multithread [don't] use extra threads with in-core multi-threading\n" +" help show this help message\n"); + return 1; + } else if (strcasecmp(tok, "compute_bound") == 0) { + *min_sockets = 1; + *max_sockets = INT_MAX; + *min_cores = 1; + *max_cores = INT_MAX; + *cpu_bind_type |= CPU_BIND_TO_CORES; + } else if (strcasecmp(tok, "memory_bound") == 0) { + *min_cores = 1; + *max_cores = 1; + *cpu_bind_type |= CPU_BIND_TO_CORES; + } else if (strcasecmp(tok, "multithread") == 0) { + *min_threads = 1; + *max_threads = INT_MAX; + *cpu_bind_type |= CPU_BIND_TO_THREADS; + } else if (strcasecmp(tok, "nomultithread") == 0) { + *min_threads = 1; + *max_threads = 1; + *cpu_bind_type |= CPU_BIND_TO_THREADS; + } else { + error("unrecognized --hint argument \"%s\", see --hint=help", tok); + xfree(buf); + return 1; + } + } + + xfree(buf); + return 0; +} + +uint16_t parse_mail_type(const char *arg) +{ + uint16_t rc; + + if (strcasecmp(arg, "BEGIN") == 0) + rc = MAIL_JOB_BEGIN; + else if (strcasecmp(arg, "END") == 0) + rc = MAIL_JOB_END; + else if (strcasecmp(arg, "FAIL") == 0) + rc = MAIL_JOB_FAIL; + else if (strcasecmp(arg, "ALL") == 0) + rc = MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL; + else + rc = 0; /* failure */ + + return rc; +} +char *print_mail_type(const uint16_t type) +{ + if (type == 0) + return "NONE"; + + if (type == MAIL_JOB_BEGIN) + return "BEGIN"; + if (type == MAIL_JOB_END) + return "END"; + if (type == MAIL_JOB_FAIL) + return "FAIL"; + if (type == (MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL)) + return "ALL"; + + return "MULTIPLE"; +} + +static void +_freeF(void *data) +{ + xfree(data); +} + +static List +_create_path_list(void) +{ + List l = list_create(_freeF); + char *path = xstrdup(getenv("PATH")); + char *c, *lc; + + if (!path) { + error("Error in PATH environment variable"); + list_destroy(l); + return NULL; + } + + c = lc = path; + + while (*c != '\0') { + if (*c == ':') { + /* nullify and push token onto list */ + *c = '\0'; + if (lc != NULL && strlen(lc) > 0) + list_append(l, xstrdup(lc)); + lc = ++c; + } else + c++; + } + + if (strlen(lc) > 0) + list_append(l, xstrdup(lc)); + + xfree(path); + + return l; +} + +char * +search_path(char *cwd, char *cmd, bool check_current_dir, int access_mode) +{ + List l = _create_path_list(); + ListIterator i = NULL; + char *path, *fullpath = NULL; + + if (l == NULL) + return NULL; + + if ( (cmd[0] == '.' || cmd[0] == '/') + && (access(cmd, access_mode) == 0 ) ) { + if (cmd[0] == '.') + xstrfmtcat(fullpath, "%s/", cwd); + xstrcat(fullpath, cmd); + goto done; + } + + if (check_current_dir) + list_prepend(l, xstrdup(cwd)); + + i = list_iterator_create(l); + while ((path = list_next(i))) { + xstrfmtcat(fullpath, "%s/%s", path, cmd); + + if (access(fullpath, access_mode) == 0) + goto done; + + xfree(fullpath); + fullpath = NULL; + } + done: + list_destroy(l); + return fullpath; +} + +char *print_commandline(const int script_argc, char **script_argv) +{ + int i; + char buf[256]; + + buf[0] = '\0'; + for (i = 0; i < script_argc; i++) + snprintf(buf, 256, "%s", script_argv[i]); + return xstrdup(buf); +} + +char *print_geometry(const uint16_t *geometry) +{ + int i; + char buf[32], *rc = NULL; + + if ((SYSTEM_DIMENSIONS == 0) + || (geometry[0] == (uint16_t)NO_VAL)) + return NULL; + + for (i=0; i<SYSTEM_DIMENSIONS; i++) { + if (i > 0) + snprintf(buf, sizeof(buf), "x%u", geometry[i]); + else + snprintf(buf, sizeof(buf), "%u", geometry[i]); + xstrcat(rc, buf); + } + + return rc; +} diff --git a/src/common/proc_args.h b/src/common/proc_args.h new file mode 100644 index 00000000000..109556bad90 --- /dev/null +++ b/src/common/proc_args.h @@ -0,0 +1,106 @@ +/*****************************************************************************\ + * proc_args.h - helper functions for command argument processing + * $Id: opt.h 11996 2007-08-10 20:36:26Z jette $ + ***************************************************************************** + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. + * Written by Christopher Holmes <cholmes@hp.com>, who borrowed heavily + * from existing SLURM source code, particularly src/srun/opt.c + * + * This file is part of SLURM, a resource management program. + * For details, see <http://www.llnl.gov/linux/slurm/>. + * + * 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. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +\*****************************************************************************/ + +#ifndef _PROC_ARGS_H +#define _PROC_ARGS_H + + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <sys/types.h> +#include <unistd.h> + +#include "src/common/macros.h" /* true and false */ +#include "src/common/env.h" + + +#define format_task_dist_states(t) (t == SLURM_DIST_BLOCK) ? "block" : \ + (t == SLURM_DIST_CYCLIC) ? "cyclic" : \ + (t == SLURM_DIST_PLANE) ? "plane" : \ + (t == SLURM_DIST_CYCLIC_CYCLIC) ? "cyclic:cyclic" : \ + (t == SLURM_DIST_CYCLIC_BLOCK) ? "cyclic:block" : \ + (t == SLURM_DIST_BLOCK_CYCLIC) ? "block:cyclic" : \ + (t == SLURM_DIST_BLOCK_BLOCK) ? "block:block" : \ + (t == SLURM_DIST_ARBITRARY) ? "arbitrary" : \ + "unknown" + + +/* print this version of SLURM */ +void print_slurm_version(void); + +/* verify the requested distribution type */ +task_dist_states_t verify_dist_type(const char *arg, uint32_t *plane_size); + +/* verify the requested connection type */ +int verify_conn_type(const char *arg); + +/* verify the requested geometry arguments */ +int verify_geometry(const char *arg, uint16_t *geometry); + +/* return command name from its full path name */ +char * base_name(char* command); + +/* confirm and convert a str to it's presented numeric value */ +long str_to_bytes(const char *arg); + +/* verify that a node count in arg is of a known form (count or min-max) */ +bool verify_node_count(const char *arg, int *min_nodes, int *max_nodes); + +/* parse a possible range of values from the form: count, min-max, or '*' */ +bool get_resource_arg_range(const char *arg, const char *what, + int* min, int *max, bool isFatal); + +/* verify resource counts from a complex form of: X, X:X, X:X:X or X:X:X:X */ +bool verify_socket_core_thread_count(const char *arg, + int *min_sockets, int *max_sockets, + int *min_cores, int *max_cores, + int *min_threads, int *max_threads, + cpu_bind_type_t *cpu_bind_type); + +/* verify a hint and convert it into the implied settings */ +bool verify_hint(const char *arg, int *min_sockets, int *max_sockets, + int *min_cores, int *max_cores, int *min_threads, + int *max_threads, cpu_bind_type_t *cpu_bind_type); + +/* parse the mail type */ +uint16_t parse_mail_type(const char *arg); + +/* print the mail type */ +char *print_mail_type(const uint16_t type); + +/* search PATH to confirm the access of the given command */ +char *search_path(char *cwd, char *cmd, bool check_current_dir, + int access_mode); + +/* helper function for printing options */ +char *print_commandline(const int script_argc, char **script_argv); + +/* helper function for printing geometry option */ +char *print_geometry(const uint16_t *geometry); + +#endif /* !_PROC_ARGS_H */ diff --git a/src/salloc/opt.c b/src/salloc/opt.c index 82e37115133..cb527d919de 100644 --- a/src/salloc/opt.c +++ b/src/salloc/opt.c @@ -60,6 +60,7 @@ #include "src/common/list.h" #include "src/common/log.h" #include "src/common/parse_time.h" +#include "src/common/proc_args.h" #include "src/common/slurm_protocol_api.h" #include "src/common/uid.h" #include "src/common/xmalloc.h" @@ -109,6 +110,14 @@ #define LONG_OPT_NO_BELL 0x117 #define LONG_OPT_COMMENT 0x118 #define LONG_OPT_REBOOT 0x119 +#define LONG_OPT_SOCKETSPERNODE 0x130 +#define LONG_OPT_CORESPERSOCKET 0x131 +#define LONG_OPT_THREADSPERCORE 0x132 +#define LONG_OPT_NTASKSPERNODE 0x136 +#define LONG_OPT_NTASKSPERSOCKET 0x137 +#define LONG_OPT_NTASKSPERCORE 0x138 +#define LONG_OPT_JOBMEM 0x13a +#define LONG_OPT_HINT 0x13b /*---- global variables, defined in opt.h ----*/ opt_t opt; @@ -117,9 +126,6 @@ opt_t opt; typedef struct env_vars env_vars_t; -/* return command name from its full path name */ -static char * _base_name(char* command); - /* Get a decimal integer from arg */ static int _get_int(const char *arg, const char *what); @@ -139,16 +145,9 @@ static void _opt_list(void); /* verify options sanity */ static bool _opt_verify(void); -static void _print_version(void); static void _process_env_var(env_vars_t *e, const char *val); -static uint16_t _parse_mail_type(const char *arg); -static char *_print_mail_type(const uint16_t type); static int _parse_signal(const char *signal_name); -static long _to_bytes(const char *arg); static void _usage(void); -static bool _verify_node_count(const char *arg, int *min, int *max); -static int _verify_geometry(const char *arg, uint16_t *geometry); -static int _verify_conn_type(const char *arg); /*---[ end forward declarations of static functions ]---------------------*/ @@ -170,216 +169,6 @@ int initialize_and_process_args(int argc, char *argv[]) } -static void _print_version(void) -{ - printf("%s %s\n", PACKAGE, SLURM_VERSION); -} - -/* - * verify that a connection type in arg is of known form - * returns the connection_type or -1 if not recognized - */ -static int _verify_conn_type(const char *arg) -{ - int len = strlen(arg); - - if (!strncasecmp(arg, "MESH", len)) - return SELECT_MESH; - else if (!strncasecmp(arg, "TORUS", len)) - return SELECT_TORUS; - else if (!strncasecmp(arg, "NAV", len)) - return SELECT_NAV; - - error("invalid --conn-type argument %s ignored.", arg); - return -1; -} - -/* - * verify geometry arguments, must have proper count - * returns -1 on error, 0 otherwise - */ -static int _verify_geometry(const char *arg, uint16_t *geometry) -{ - char* token, *delimiter = ",x", *next_ptr; - int i, rc = 0; - char* geometry_tmp = xstrdup(arg); - char* original_ptr = geometry_tmp; - - token = strtok_r(geometry_tmp, delimiter, &next_ptr); - for (i=0; i<SYSTEM_DIMENSIONS; i++) { - if (token == NULL) { - error("insufficient dimensions in --geometry"); - rc = -1; - break; - } - geometry[i] = (uint16_t)atoi(token); - if (geometry[i] == 0 || geometry[i] == (uint16_t)NO_VAL) { - error("invalid --geometry argument"); - rc = -1; - break; - } - geometry_tmp = next_ptr; - token = strtok_r(geometry_tmp, delimiter, &next_ptr); - } - if (token != NULL) { - error("too many dimensions in --geometry"); - rc = -1; - } - - if (original_ptr) - xfree(original_ptr); - - return rc; -} - -/* Convert a string into a node count */ -static int -_str_to_nodes(const char *num_str, char **leftover) -{ - long int num; - char *endptr; - - num = strtol(num_str, &endptr, 10); - if (endptr == num_str) { /* no valid digits */ - *leftover = (char *)num_str; - return 0; - } - if (*endptr != '\0' && (*endptr == 'k' || *endptr == 'K')) { - num *= 1024; - endptr++; - } - *leftover = endptr; - - return (int)num; -} - -/* - * verify that a node count in arg is of a known form (count or min-max) - * OUT min, max specified minimum and maximum node counts - * RET true if valid - */ -static bool -_verify_node_count(const char *arg, int *min_nodes, int *max_nodes) -{ - char *ptr, *min_str, *max_str; - char *leftover; - - /* Does the string contain a "-" character? If so, treat as a range. - * otherwise treat as an absolute node count. */ - if ((ptr = index(arg, '-')) != NULL) { - min_str = xstrndup(arg, ptr-arg); - *min_nodes = _str_to_nodes(min_str, &leftover); - if (!xstring_is_whitespace(leftover)) { - error("\"%s\" is not a valid node count", min_str); - xfree(min_str); - return false; - } - xfree(min_str); - if (*min_nodes == 0) - *min_nodes = 1; - - max_str = xstrndup(ptr+1, strlen(arg)-((ptr+1)-arg)); - *max_nodes = _str_to_nodes(max_str, &leftover); - if (!xstring_is_whitespace(leftover)) { - error("\"%s\" is not a valid node count", max_str); - xfree(max_str); - return false; - } - xfree(max_str); - } else { - *min_nodes = *max_nodes = _str_to_nodes(arg, &leftover); - if (!xstring_is_whitespace(leftover)) { - error("\"%s\" is not a valid node count", arg); - return false; - } - if (*min_nodes == 0) { - /* whitespace does not a valid node count make */ - error("\"%s\" is not a valid node count", arg); - return false; - } - } - - if ((*max_nodes != 0) && (*max_nodes < *min_nodes)) { - error("Maximum node count %d is less than" - " minimum node count %d", - *max_nodes, *min_nodes); - return false; - } - - return true; -} - -/* return command name from its full path name */ -static char * _base_name(char* command) -{ - char *char_ptr, *name; - int i; - - if (command == NULL) - return NULL; - - char_ptr = strrchr(command, (int)'/'); - if (char_ptr == NULL) - char_ptr = command; - else - char_ptr++; - - i = strlen(char_ptr); - name = xmalloc(i+1); - strcpy(name, char_ptr); - return name; -} - -/* - * _to_bytes(): verify that arg is numeric with optional "G" or "M" at end - * if "G" or "M" is there, multiply by proper power of 2 and return - * number in bytes - */ -static long _to_bytes(const char *arg) -{ - char *buf; - char *endptr; - int end; - int multiplier = 1; - long result; - - buf = xstrdup(arg); - - end = strlen(buf) - 1; - - if (isdigit(buf[end])) { - result = strtol(buf, &endptr, 10); - - if (*endptr != '\0') - result = -result; - - } else { - - switch (toupper(buf[end])) { - - case 'G': - multiplier = 1024; - break; - - case 'M': - /* do nothing */ - break; - - default: - multiplier = -1; - } - - buf[end] = '\0'; - - result = multiplier * strtol(buf, &endptr, 10); - - if (*endptr != '\0') - result = -result; - } - - return result; -} - /* * print error message to stderr with opt.progname prepended */ @@ -426,6 +215,16 @@ static void _opt_default() opt.min_nodes = 1; opt.max_nodes = 0; opt.nodes_set = false; + opt.min_sockets_per_node = NO_VAL; /* requested min/maxsockets */ + opt.max_sockets_per_node = NO_VAL; + opt.min_cores_per_socket = NO_VAL; /* requested min/maxcores */ + opt.max_cores_per_socket = NO_VAL; + opt.min_threads_per_core = NO_VAL; /* requested min/maxthreads */ + opt.max_threads_per_core = NO_VAL; + opt.ntasks_per_node = NO_VAL; /* ntask max limits */ + opt.ntasks_per_socket = NO_VAL; + opt.ntasks_per_core = NO_VAL; + opt.cpu_bind_type = 0; /* local dummy variable for now */ opt.time_limit = NO_VAL; opt.time_limit_str = NULL; opt.partition = NULL; @@ -436,6 +235,9 @@ static void _opt_default() opt.account = NULL; opt.comment = NULL; + opt.distribution = SLURM_DIST_UNKNOWN; + opt.plane_size = NO_VAL; + opt.shared = (uint16_t)NO_VAL; opt.no_kill = false; opt.kill_command_signal = SIGTERM; @@ -453,6 +255,7 @@ static void _opt_default() opt.minsockets = -1; opt.mincores = -1; opt.minthreads = -1; + opt.jobmem = -1; opt.realmem = -1; opt.tmpdisk = -1; @@ -579,16 +382,16 @@ _process_env_var(env_vars_t *e, const char *val) break; case OPT_NODES: - opt.nodes_set = _verify_node_count( val, - &opt.min_nodes, - &opt.max_nodes ); + opt.nodes_set = verify_node_count( val, + &opt.min_nodes, + &opt.max_nodes ); if (opt.nodes_set == false) { error("invalid node count in env variable, ignoring"); } break; case OPT_CONN_TYPE: - opt.conn_type = _verify_conn_type(val); + opt.conn_type = verify_conn_type(val); break; case OPT_NO_ROTATE: @@ -596,7 +399,7 @@ _process_env_var(env_vars_t *e, const char *val) break; case OPT_GEOMETRY: - if (_verify_geometry(val, opt.geometry)) { + if (verify_geometry(val, opt.geometry)) { error("\"%s=%s\" -- invalid geometry, ignoring...", e->var, val); } @@ -653,6 +456,7 @@ void set_options(const int argc, char **argv) int opt_char, option_index = 0; char *tmp; static struct option long_options[] = { + {"extra-node-info", required_argument, 0, 'B'}, {"cpus-per-task", required_argument, 0, 'c'}, {"constraint", required_argument, 0, 'C'}, {"dependency", required_argument, 0, 'd'}, @@ -664,7 +468,8 @@ void set_options(const int argc, char **argv) {"job-name", required_argument, 0, 'J'}, {"no-kill", no_argument, 0, 'k'}, {"kill-command", optional_argument, 0, 'K'}, - {"tasks", required_argument, 0, 'n'}, + {"distribution", required_argument, 0, 'm'}, + {"ntasks", required_argument, 0, 'n'}, {"nodes", required_argument, 0, 'N'}, {"overcommit", no_argument, 0, 'O'}, {"partition", required_argument, 0, 'p'}, @@ -686,6 +491,15 @@ void set_options(const int argc, char **argv) {"mincores", required_argument, 0, LONG_OPT_MINCORES}, {"minthreads", required_argument, 0, LONG_OPT_MINTHREADS}, {"mem", required_argument, 0, LONG_OPT_MEM}, + {"job-mem", required_argument, 0, LONG_OPT_JOBMEM}, + {"hint", required_argument, 0, LONG_OPT_HINT}, + {"sockets-per-node", required_argument, 0, LONG_OPT_SOCKETSPERNODE}, + {"cores-per-socket", required_argument, 0, LONG_OPT_CORESPERSOCKET}, + {"threads-per-core", required_argument, 0, LONG_OPT_THREADSPERCORE}, + {"ntasks-per-node", required_argument, 0, LONG_OPT_NTASKSPERNODE}, + {"ntasks-per-socket",required_argument, 0, LONG_OPT_NTASKSPERSOCKET}, + {"ntasks-per-core", required_argument, 0, LONG_OPT_NTASKSPERCORE}, + {"tasks-per-node", required_argument, 0, LONG_OPT_NTASKSPERNODE}, {"tmp", required_argument, 0, LONG_OPT_TMP}, {"uid", required_argument, 0, LONG_OPT_UID}, {"gid", required_argument, 0, LONG_OPT_GID}, @@ -701,7 +515,7 @@ void set_options(const int argc, char **argv) {"reboot", no_argument, 0, LONG_OPT_REBOOT}, {NULL, 0, 0, 0} }; - char *opt_string = "+a:c:C:d:F:g:hHIJ:kK::n:N:Op:qR:st:uU:vVw:W:x:"; + char *opt_string = "+a:B:c:C:d:F:g:hHIJ:kK:m:n:N:Op:qR:st:uU:vVw:W:x:"; opt.progname = xbasename(argv[0]); optind = 0; @@ -714,6 +528,24 @@ void set_options(const int argc, char **argv) "information\n"); exit(1); break; + case 'B': + opt.extra_set = verify_socket_core_thread_count( + optarg, + &opt.min_sockets_per_node, + &opt.max_sockets_per_node, + &opt.min_cores_per_socket, + &opt.max_cores_per_socket, + &opt.min_threads_per_core, + &opt.max_threads_per_core, + &opt.cpu_bind_type); + + + if (opt.extra_set == false) { + error("invalid resource allocation -B `%s'", + optarg); + exit(1); + } + break; case 'c': opt.cpus_set = true; opt.cpus_per_task = @@ -738,7 +570,7 @@ void set_options(const int argc, char **argv) } break; case 'g': - if (_verify_geometry(optarg, opt.geometry)) + if (verify_geometry(optarg, opt.geometry)) exit(1); break; case 'h': @@ -765,6 +597,15 @@ void set_options(const int argc, char **argv) } opt.kill_command_signal_set = true; break; + case 'm': + opt.distribution = verify_dist_type(optarg, + &opt.plane_size); + if (opt.distribution == SLURM_DIST_UNKNOWN) { + error("distribution type `%s' " + "is not recognized", optarg); + exit(1); + } + break; case 'n': opt.nprocs_set = true; opt.nprocs = @@ -772,9 +613,9 @@ void set_options(const int argc, char **argv) break; case 'N': opt.nodes_set = - _verify_node_count(optarg, - &opt.min_nodes, - &opt.max_nodes); + verify_node_count(optarg, + &opt.min_nodes, + &opt.max_nodes); if (opt.nodes_set == false) { exit(1); } @@ -810,7 +651,7 @@ void set_options(const int argc, char **argv) opt.verbose++; break; case 'V': - _print_version(); + print_slurm_version(); exit(0); break; case 'w': @@ -869,15 +710,23 @@ void set_options(const int argc, char **argv) } break; case LONG_OPT_MEM: - opt.realmem = (int) _to_bytes(optarg); + opt.realmem = (int) str_to_bytes(optarg); if (opt.realmem < 0) { error("invalid memory constraint %s", optarg); exit(1); } break; + case LONG_OPT_JOBMEM: + opt.jobmem = (int) str_to_bytes(optarg); + if (opt.jobmem < 0) { + error("invalid memory constraint %s", + optarg); + exit(1); + } + break; case LONG_OPT_TMP: - opt.tmpdisk = _to_bytes(optarg); + opt.tmpdisk = str_to_bytes(optarg); if (opt.tmpdisk < 0) { error("invalid tmp value %s", optarg); exit(1); @@ -894,7 +743,7 @@ void set_options(const int argc, char **argv) fatal ("--gid=\"%s\" invalid", optarg); break; case LONG_OPT_CONNTYPE: - opt.conn_type = _verify_conn_type(optarg); + opt.conn_type = verify_conn_type(optarg); break; case LONG_OPT_BEGIN: opt.begin = parse_time(optarg); @@ -905,7 +754,7 @@ void set_options(const int argc, char **argv) } break; case LONG_OPT_MAIL_TYPE: - opt.mail_type |= _parse_mail_type(optarg); + opt.mail_type |= parse_mail_type(optarg); if (opt.mail_type == 0) fatal("--mail-type=%s invalid", optarg); break; @@ -937,6 +786,48 @@ void set_options(const int argc, char **argv) xfree(opt.comment); opt.comment = xstrdup(optarg); break; + case LONG_OPT_SOCKETSPERNODE: + get_resource_arg_range( optarg, "sockets-per-node", + &opt.min_sockets_per_node, + &opt.max_sockets_per_node, + true ); + break; + case LONG_OPT_CORESPERSOCKET: + get_resource_arg_range( optarg, "cores-per-socket", + &opt.min_cores_per_socket, + &opt.max_cores_per_socket, + true); + break; + case LONG_OPT_THREADSPERCORE: + get_resource_arg_range( optarg, "threads-per-core", + &opt.min_threads_per_core, + &opt.max_threads_per_core, + true ); + break; + case LONG_OPT_HINT: + if (verify_hint(optarg, + &opt.min_sockets_per_node, + &opt.max_sockets_per_node, + &opt.min_cores_per_socket, + &opt.max_cores_per_socket, + &opt.min_threads_per_core, + &opt.max_threads_per_core, + &opt.cpu_bind_type)) { + exit(1); + } + break; + case LONG_OPT_NTASKSPERNODE: + opt.ntasks_per_node = _get_int(optarg, + "ntasks-per-node"); + break; + case LONG_OPT_NTASKSPERSOCKET: + opt.ntasks_per_socket = _get_int(optarg, + "ntasks-per-socket"); + break; + case LONG_OPT_NTASKSPERCORE: + opt.ntasks_per_core = _get_int(optarg, + "ntasks-per-core"); + break; case LONG_OPT_REBOOT: opt.reboot = true; break; @@ -990,7 +881,7 @@ static bool _opt_verify(void) opt.mincpus = opt.cpus_per_task; if ((opt.job_name == NULL) && (command_argc > 0)) - opt.job_name = _base_name(command_argv[0]); + opt.job_name = base_name(command_argv[0]); if (command_argc == 0) { error("A local command is a required parameter!"); @@ -1018,11 +909,87 @@ static bool _opt_verify(void) verified = false; } + /* When CR with memory as a CR is enabled we need to assign + adequate value or check the value to opt.mem */ + if ((opt.realmem >= -1) && (opt.jobmem > 0)) { + if (opt.realmem == -1) { + opt.realmem = opt.jobmem; + } else if (opt.realmem < opt.jobmem) { + info("mem < job-mem - resizing mem to be equal to job-mem"); + opt.realmem = opt.jobmem; + } + } + + /* Check to see if user has specified enough resources to + * satisfy the plane distribution with the specified + * plane_size. + * if (n/plane_size < N) and ((N-1) * plane_size >= n) --> + * problem Simple check will not catch all the problem/invalid + * cases. + * The limitations of the plane distribution in the cons_res + * environment are more extensive and are documented in the + * SLURM reference guide. */ + if (opt.distribution == SLURM_DIST_PLANE && opt.plane_size) { + if ((opt.nprocs/opt.plane_size) < opt.min_nodes) { + if (((opt.min_nodes-1)*opt.plane_size) >= opt.nprocs) { +#if(0) + info("Too few processes ((n/plane_size) %d < N %d) " + "and ((N-1)*(plane_size) %d >= n %d)) ", + opt.nprocs/opt.plane_size, opt.min_nodes, + (opt.min_nodes-1)*opt.plane_size, opt.nprocs); +#endif + error("Too few processes for the requested " + "{plane,node} distribution"); + exit(1); + } + } + } + + /* bound max_threads/cores from ntasks_cores/sockets */ + if ((opt.max_threads_per_core <= 0) && + (opt.ntasks_per_core > 0)) { + opt.max_threads_per_core = opt.ntasks_per_core; + /* if cpu_bind_type doesn't already have a auto pref, + * choose the level based on the level of ntasks + */ + if (!(opt.cpu_bind_type & (CPU_BIND_TO_SOCKETS | + CPU_BIND_TO_CORES | + CPU_BIND_TO_THREADS))) { + opt.cpu_bind_type |= CPU_BIND_TO_CORES; + } + } + if ((opt.max_cores_per_socket <= 0) && + (opt.ntasks_per_socket > 0)) { + opt.max_cores_per_socket = opt.ntasks_per_socket; + /* if cpu_bind_type doesn't already have a auto pref, + * choose the level based on the level of ntasks + */ + if (!(opt.cpu_bind_type & (CPU_BIND_TO_SOCKETS | + CPU_BIND_TO_CORES | + CPU_BIND_TO_THREADS))) { + opt.cpu_bind_type |= CPU_BIND_TO_SOCKETS; + } + } + /* massage the numbers */ if (opt.nodes_set && !opt.nprocs_set) { /* 1 proc / node default */ opt.nprocs = opt.min_nodes; + /* 1 proc / min_[socket * core * thread] default */ + if (opt.min_sockets_per_node > 0) { + opt.nprocs *= opt.min_sockets_per_node; + opt.nprocs_set = true; + } + if (opt.min_cores_per_socket > 0) { + opt.nprocs *= opt.min_cores_per_socket; + opt.nprocs_set = true; + } + if (opt.min_threads_per_core > 0) { + opt.nprocs *= opt.min_threads_per_core; + opt.nprocs_set = true; + } + } else if (opt.nodes_set && opt.nprocs_set) { /* @@ -1074,40 +1041,6 @@ static bool _opt_verify(void) return verified; } -static uint16_t _parse_mail_type(const char *arg) -{ - uint16_t rc; - - if (strcasecmp(arg, "BEGIN") == 0) - rc = MAIL_JOB_BEGIN; - else if (strcasecmp(arg, "END") == 0) - rc = MAIL_JOB_END; - else if (strcasecmp(arg, "FAIL") == 0) - rc = MAIL_JOB_FAIL; - else if (strcasecmp(arg, "ALL") == 0) - rc = MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL; - else - rc = 0; /* failure */ - - return rc; -} -static char *_print_mail_type(const uint16_t type) -{ - if (type == 0) - return "NONE"; - - if (type == MAIL_JOB_BEGIN) - return "BEGIN"; - if (type == MAIL_JOB_END) - return "END"; - if (type == MAIL_JOB_FAIL) - return "FAIL"; - if (type == (MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL)) - return "ALL"; - - return "MULTIPLE"; -} - /* helper function for printing options * * warning: returns pointer to memory allocated on the stack. @@ -1119,9 +1052,21 @@ static char *print_constraints() if (opt.mincpus > 0) xstrfmtcat(buf, "mincpus=%d ", opt.mincpus); + if (opt.minsockets > 0) + xstrfmtcat(buf, "minsockets=%d ", opt.minsockets); + + if (opt.mincores > 0) + xstrfmtcat(buf, "mincores=%d ", opt.mincores); + + if (opt.minthreads > 0) + xstrfmtcat(buf, "minthreads=%d ", opt.minthreads); + if (opt.realmem > 0) xstrfmtcat(buf, "mem=%dM ", opt.realmem); + if (opt.jobmem > 0) + xstrfmtcat(buf, "job-mem=%dM ", opt.jobmem); + if (opt.tmpdisk > 0) xstrfmtcat(buf, "tmp=%ld ", opt.tmpdisk); @@ -1140,39 +1085,6 @@ static char *print_constraints() return buf; } -static char * -print_commandline() -{ - int i; - char buf[256]; - - buf[0] = '\0'; - for (i = 0; i < command_argc; i++) - snprintf(buf, 256, "%s", command_argv[i]); - return xstrdup(buf); -} - -static char * -print_geometry() -{ - int i; - char buf[32], *rc = NULL; - - if ((SYSTEM_DIMENSIONS == 0) - || (opt.geometry[0] == (uint16_t)NO_VAL)) - return NULL; - - for (i=0; i<SYSTEM_DIMENSIONS; i++) { - if (i > 0) - snprintf(buf, sizeof(buf), "x%u", opt.geometry[i]); - else - snprintf(buf, sizeof(buf), "%u", opt.geometry[i]); - xstrcat(rc, buf); - } - - return rc; -} - /* * Takes a string containing the number or name of a signal and returns * the signal number. The signal name is case insensitive, and may be of @@ -1255,6 +1167,9 @@ static void _opt_list() info("job name : `%s'", opt.job_name); if (opt.jobid != NO_VAL) info("jobid : %u", opt.jobid); + info("distribution : %s", format_task_dist_states(opt.distribution)); + if(opt.distribution == SLURM_DIST_PLANE) + info("plane size : %u", opt.plane_size); info("verbose : %d", opt.verbose); info("immediate : %s", tf_(opt.immediate)); info("overcommit : %s", tf_(opt.overcommit)); @@ -1276,7 +1191,7 @@ static void _opt_list() xfree(str); if (opt.conn_type >= 0) info("conn_type : %u", opt.conn_type); - str = print_geometry(); + str = print_geometry(opt.geometry); info("geometry : %s", str); xfree(str); info("reboot : %s", opt.reboot ? "no" : "yes"); @@ -1286,9 +1201,19 @@ static void _opt_list() slurm_make_time_str(&opt.begin, time_str, sizeof(time_str)); info("begin : %s", time_str); } - info("mail_type : %s", _print_mail_type(opt.mail_type)); + info("mail_type : %s", print_mail_type(opt.mail_type)); info("mail_user : %s", opt.mail_user); - str = print_commandline(); + info("sockets-per-node : %d - %d", opt.min_sockets_per_node, + opt.max_sockets_per_node); + info("cores-per-socket : %d - %d", opt.min_cores_per_socket, + opt.max_cores_per_socket); + info("threads-per-core : %d - %d", opt.min_threads_per_core, + opt.max_threads_per_core); + info("ntasks-per-node : %d", opt.ntasks_per_node); + info("ntasks-per-socket : %d", opt.ntasks_per_socket); + info("ntasks-per-core : %d", opt.ntasks_per_core); + info("plane_size : %u", opt.plane_size); + str = print_commandline(command_argc, command_argv); info("user command : `%s'", str); xfree(str); @@ -1316,6 +1241,8 @@ static void _usage(void) static void _help(void) { + slurm_ctl_conf_t *conf; + printf ( "Usage: salloc [OPTIONS...] executable [args...]\n" "\n" @@ -1323,6 +1250,7 @@ static void _help(void) " -N, --nodes=N number of nodes on which to run (N = min[-max])\n" " -n, --tasks=N number of processors required\n" " -c, --cpus-per-task=ncpus number of cpus required per task\n" +" --ntasks-per-node=n number of tasks to invoke on each node\n" " -p, --partition=partition partition requested\n" " -H, --hold submit job in held state\n" " -t, --time=minutes time limit\n" @@ -1331,6 +1259,8 @@ static void _help(void) " -K, --kill-command[=signal] signal to send terminating job\n" " -O, --overcommit overcommit resources\n" " -s, --share share nodes with other jobs\n" +" -m, --distribution=type distribution method for processes to nodes\n" +" (type = block|cyclic|arbitrary)\n" " -J, --job-name=jobname name of job\n" " --jobid=id specify jobid to use\n" " -W, --wait=sec seconds to wait for allocation if not\n" @@ -1365,8 +1295,32 @@ static void _help(void) "Consumable resources related options:\n" " --exclusive allocate nodes in exclusive mode when\n" " cpu consumable resource is enabled\n" +" --job-mem=MB maximum amount of real memory per node\n" +" required by the job.\n" +" --mem >= --job-mem if --mem is specified.\n" "\n" +"Affinity/Multi-core options: (when the task/affinity plugin is enabled)\n" +" -B --extra-node-info=S[:C[:T]] Expands to:\n" +" --sockets-per-node=S number of sockets per node to allocate\n" +" --cores-per-socket=C number of cores per socket to allocate\n" +" --threads-per-core=T number of threads per core to allocate\n" +" each field can be 'min[-max]' or wildcard '*'\n" +" total cpus requested = (N x S x C x T)\n" +"\n" +" --ntasks-per-socket=n number of tasks to invoke on each socket\n" +" --ntasks-per-core=n number of tasks to invoke on each core\n"); + conf = slurm_conf_lock(); + if (conf->task_plugin != NULL + && strcasecmp(conf->task_plugin, "task/affinity") == 0) { + printf( +" --hint= Bind tasks according to application hints\n" +" (see \"--hint=help\" for options)\n"); + } + slurm_conf_unlock(); + + printf("\n" #ifdef HAVE_BG /* Blue gene specific options */ +"\n" "Blue Gene related options:\n" " -g, --geometry=XxYxZ geometry constraints of the job\n" " -R, --no-rotate disable geometry rotation\n" diff --git a/src/salloc/opt.h b/src/salloc/opt.h index c101b0bcb4d..ed974a07042 100644 --- a/src/salloc/opt.h +++ b/src/salloc/opt.h @@ -62,11 +62,25 @@ typedef struct salloc_options { int min_nodes; /* --nodes=n, -N n */ int max_nodes; /* --nodes=x-n, -N x-n */ bool nodes_set; /* true if nodes explicitly set */ + int min_sockets_per_node; /* --sockets-per-node=n */ + int max_sockets_per_node; /* --sockets-per-node=x-n */ + int min_cores_per_socket; /* --cores-per-socket=n */ + int max_cores_per_socket; /* --cores-per-socket=x-n */ + int min_threads_per_core; /* --threads-per-core=n */ + int max_threads_per_core; /* --threads-per-core=x-n */ + int ntasks_per_node; /* --ntasks-per-node=n */ + int ntasks_per_socket; /* --ntasks-per-socket=n */ + int ntasks_per_core; /* --ntasks-per-core=n */ + cpu_bind_type_t cpu_bind_type; /* --cpu_bind= */ + bool extra_set; /* true if extra node info explicitly set */ int time_limit; /* --time, -t (int minutes) */ char *time_limit_str; /* --time, -t (string) */ char *partition; /* --partition=n, -p n */ enum task_dist_states distribution; /* --distribution=, -m dist */ + uint32_t plane_size; /* lllp distribution -> plane_size for + * when -m plane=<# of lllp per + * plane> */ char *job_name; /* --job-name=, -J name */ unsigned int jobid; /* --jobid=jobid */ unsigned int dependency;/* --dependency, -P jobid */ @@ -91,6 +105,7 @@ typedef struct salloc_options { int minsockets; /* --minsockets=n */ int mincores; /* --mincores=n */ int minthreads; /* --minthreads=n */ + int jobmem; /* --job-mem=n */ int realmem; /* --mem=n */ long tmpdisk; /* --tmp=n */ char *constraints; /* --constraints=, -C constraint*/ diff --git a/src/salloc/salloc.c b/src/salloc/salloc.c index 06da0579f86..7b13e4e9e1d 100644 --- a/src/salloc/salloc.c +++ b/src/salloc/salloc.c @@ -249,6 +249,9 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->user_id = opt.uid; desc->group_id = opt.gid; desc->dependency = opt.dependency; + desc->task_dist = opt.distribution; + if (opt.plane_size != NO_VAL) + desc->plane_size = opt.plane_size; if (opt.nice) desc->nice = NICE_OFFSET + opt.nice; desc->mail_type = opt.mail_type; @@ -276,6 +279,8 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->reboot = 1; if (opt.no_rotate) desc->rotate = 0; + + /* job constraints */ if (opt.mincpus > -1) desc->job_min_procs = opt.mincpus; if (opt.minsockets > -1) @@ -284,6 +289,8 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->job_min_cores = opt.mincores; if (opt.minthreads > -1) desc->job_min_threads = opt.minthreads; + if (opt.jobmem > -1) + desc->job_max_memory = opt.jobmem; if (opt.realmem > -1) desc->job_min_memory = opt.realmem; if (opt.tmpdisk > -1) @@ -297,6 +304,27 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->num_tasks = opt.nprocs; if (opt.cpus_set) desc->cpus_per_task = opt.cpus_per_task; + if (opt.ntasks_per_node > -1) + desc->ntasks_per_node = opt.ntasks_per_node; + if (opt.ntasks_per_socket > -1) + desc->ntasks_per_socket = opt.ntasks_per_socket; + if (opt.ntasks_per_core > -1) + desc->ntasks_per_core = opt.ntasks_per_core; + + /* node constraints */ + if (opt.min_sockets_per_node > -1) + desc->min_sockets = opt.min_sockets_per_node; + if (opt.max_sockets_per_node > -1) + desc->max_sockets = opt.max_sockets_per_node; + if (opt.min_cores_per_socket > -1) + desc->min_cores = opt.min_cores_per_socket; + if (opt.max_cores_per_socket > -1) + desc->max_cores = opt.max_cores_per_socket; + if (opt.min_threads_per_core > -1) + desc->min_threads = opt.min_threads_per_core; + if (opt.max_threads_per_core > -1) + desc->max_threads = opt.max_threads_per_core; + if (opt.no_kill) desc->kill_on_node_fail = 0; if (opt.time_limit != NO_VAL) diff --git a/src/sbatch/opt.c b/src/sbatch/opt.c index 3b43bf05335..41bd06b7afe 100644 --- a/src/sbatch/opt.c +++ b/src/sbatch/opt.c @@ -60,6 +60,7 @@ #include "src/common/list.h" #include "src/common/log.h" #include "src/common/parse_time.h" +#include "src/common/proc_args.h" #include "src/common/slurm_protocol_api.h" #include "src/common/uid.h" #include "src/common/xmalloc.h" @@ -105,12 +106,19 @@ #define LONG_OPT_NO_REQUEUE 0x116 #define LONG_OPT_COMMENT 0x117 #define LONG_OPT_WRAP 0x118 +#define LONG_OPT_SOCKETSPERNODE 0x130 +#define LONG_OPT_CORESPERSOCKET 0x131 +#define LONG_OPT_THREADSPERCORE 0x132 +#define LONG_OPT_NTASKSPERNODE 0x136 +#define LONG_OPT_NTASKSPERSOCKET 0x137 +#define LONG_OPT_NTASKSPERCORE 0x138 +#define LONG_OPT_JOBMEM 0x13a +#define LONG_OPT_HINT 0x13b #define LONG_OPT_BLRTS_IMAGE 0x140 #define LONG_OPT_LINUX_IMAGE 0x141 #define LONG_OPT_MLOADER_IMAGE 0x142 #define LONG_OPT_RAMDISK_IMAGE 0x143 #define LONG_OPT_REBOOT 0x144 -#define LONG_OPT_TASKSPERNODE 0x145 #define LONG_OPT_GET_USER_ENV 0x146 /*---- global variables, defined in opt.h ----*/ @@ -120,10 +128,6 @@ opt_t opt; typedef struct env_vars env_vars_t; -/* return command name from its full path name */ -static char * _base_name(char* command); - -static List _create_path_list(void); /* Get a decimal integer from arg */ static int _get_int(const char *arg, const char *what); @@ -148,23 +152,11 @@ static void _opt_list(void); /* verify options sanity */ static bool _opt_verify(void); -static void _print_version(void); - static void _process_env_var(env_vars_t *e, const char *val); -static uint16_t _parse_mail_type(const char *arg); static uint16_t _parse_pbs_mail_type(const char *arg); -static char *_print_mail_type(const uint16_t type); - -/* search PATH for command returns full path */ -static char *_search_path(char *, bool, int); - -static long _to_bytes(const char *arg); static void _usage(void); -static bool _verify_node_count(const char *arg, int *min, int *max); -static int _verify_geometry(const char *arg, uint16_t *geometry); -static int _verify_conn_type(const char *arg); static char *_fullpath(const char *filename); static void _set_options(int argc, char **argv); static void _set_pbs_options(int argc, char **argv); @@ -172,179 +164,6 @@ static void _parse_pbs_resource_list(char *rl); /*---[ end forward declarations of static functions ]---------------------*/ -static void _print_version(void) -{ - printf("%s %s\n", PACKAGE, SLURM_VERSION); -} - -/* - * verify that a connection type in arg is of known form - * returns the connection_type or -1 if not recognized - */ -static int _verify_conn_type(const char *arg) -{ - int len = strlen(arg); - - if (!strncasecmp(arg, "MESH", len)) - return SELECT_MESH; - else if (!strncasecmp(arg, "TORUS", len)) - return SELECT_TORUS; - else if (!strncasecmp(arg, "NAV", len)) - return SELECT_NAV; - - error("invalid --conn-type argument %s ignored.", arg); - return -1; -} - -/* - * verify geometry arguments, must have proper count - * returns -1 on error, 0 otherwise - */ -static int _verify_geometry(const char *arg, uint16_t *geometry) -{ - char* token, *delimiter = ",x", *next_ptr; - int i, rc = 0; - char* geometry_tmp = xstrdup(arg); - char* original_ptr = geometry_tmp; - - token = strtok_r(geometry_tmp, delimiter, &next_ptr); - for (i=0; i<SYSTEM_DIMENSIONS; i++) { - if (token == NULL) { - error("insufficient dimensions in --geometry"); - rc = -1; - break; - } - geometry[i] = (uint16_t)atoi(token); - if (geometry[i] == 0 || geometry[i] == (uint16_t)NO_VAL) { - error("invalid --geometry argument"); - rc = -1; - break; - } - geometry_tmp = next_ptr; - token = strtok_r(geometry_tmp, delimiter, &next_ptr); - } - if (token != NULL) { - error("too many dimensions in --geometry"); - rc = -1; - } - - if (original_ptr) - xfree(original_ptr); - - return rc; -} - -/* - * verify that a node count in arg is of a known form (count or min-max) - * OUT min, max specified minimum and maximum node counts - * RET true if valid - */ -static bool -_verify_node_count(const char *arg, int *min_nodes, int *max_nodes) -{ - char *end_ptr; - double val1, val2; - - val1 = strtod(arg, &end_ptr); - if (end_ptr[0] == 'k' || end_ptr[0] == 'K') { - val1 *= 1024; - end_ptr++; - } - - if (end_ptr[0] == '\0') { - *min_nodes = val1; - return true; - } - - if (end_ptr[0] != '-') - return false; - - val2 = strtod(&end_ptr[1], &end_ptr); - if (end_ptr[0] == 'k' || end_ptr[0] == 'K') { - val2 *= 1024; - end_ptr++; - } - - if (end_ptr[0] == '\0') { - *min_nodes = val1; - *max_nodes = val2; - return true; - } else - return false; - -} - -/* return command name from its full path name */ -static char * _base_name(char* command) -{ - char *char_ptr, *name; - int i; - - if (command == NULL) - return NULL; - - char_ptr = strrchr(command, (int)'/'); - if (char_ptr == NULL) - char_ptr = command; - else - char_ptr++; - - i = strlen(char_ptr); - name = xmalloc(i+1); - strcpy(name, char_ptr); - return name; -} - -/* - * _to_bytes(): verify that arg is numeric with optional "G" or "M" at end - * if "G" or "M" is there, multiply by proper power of 2 and return - * number in bytes - */ -static long _to_bytes(const char *arg) -{ - char *buf; - char *endptr; - int end; - int multiplier = 1; - long result; - - buf = xstrdup(arg); - - end = strlen(buf) - 1; - - if (isdigit(buf[end])) { - result = strtol(buf, &endptr, 10); - - if (*endptr != '\0') - result = -result; - - } else { - - switch (toupper(buf[end])) { - - case 'G': - multiplier = 1024; - break; - - case 'M': - /* do nothing */ - break; - - default: - multiplier = -1; - } - - buf[end] = '\0'; - - result = multiplier * strtol(buf, &endptr, 10); - - if (*endptr != '\0') - result = -result; - } - - return result; -} - /* * print error message to stderr with opt.progname prepended */ @@ -398,8 +217,17 @@ static void _opt_default() opt.cpus_set = false; opt.min_nodes = 1; opt.max_nodes = 0; - opt.tasks_per_node = -1; opt.nodes_set = false; + opt.min_sockets_per_node = NO_VAL; /* requested min/maxsockets */ + opt.max_sockets_per_node = NO_VAL; + opt.min_cores_per_socket = NO_VAL; /* requested min/maxcores */ + opt.max_cores_per_socket = NO_VAL; + opt.min_threads_per_core = NO_VAL; /* requested min/maxthreads */ + opt.max_threads_per_core = NO_VAL; + opt.ntasks_per_node = NO_VAL; /* ntask max limits */ + opt.ntasks_per_socket = NO_VAL; + opt.ntasks_per_core = NO_VAL; + opt.cpu_bind_type = 0; /* local dummy variable for now */ opt.time_limit = NO_VAL; opt.partition = NULL; @@ -410,6 +238,9 @@ static void _opt_default() opt.account = NULL; opt.comment = NULL; + opt.distribution = SLURM_DIST_UNKNOWN; + opt.plane_size = NO_VAL; + opt.shared = (uint16_t)NO_VAL; opt.no_kill = false; @@ -425,6 +256,7 @@ static void _opt_default() opt.minsockets = -1; opt.mincores = -1; opt.minthreads = -1; + opt.jobmem = -1; opt.realmem = -1; opt.tmpdisk = -1; @@ -556,9 +388,9 @@ _process_env_var(env_vars_t *e, const char *val) break; case OPT_NODES: - opt.nodes_set = _verify_node_count( val, - &opt.min_nodes, - &opt.max_nodes ); + opt.nodes_set = verify_node_count( val, + &opt.min_nodes, + &opt.max_nodes ); if (opt.nodes_set == false) { error("\"%s=%s\" -- invalid node count. ignoring...", e->var, val); @@ -566,7 +398,7 @@ _process_env_var(env_vars_t *e, const char *val) break; case OPT_CONN_TYPE: - opt.conn_type = _verify_conn_type(val); + opt.conn_type = verify_conn_type(val); break; case OPT_NO_ROTATE: @@ -574,7 +406,7 @@ _process_env_var(env_vars_t *e, const char *val) break; case OPT_GEOMETRY: - if (_verify_geometry(val, opt.geometry)) { + if (verify_geometry(val, opt.geometry)) { error("\"%s=%s\" -- invalid geometry, ignoring...", e->var, val); } @@ -602,6 +434,7 @@ static struct option long_options[] = { is only here for moab tansition doesn't do anything */ + {"extra-node-info", required_argument, 0, 'B'}, {"cpus-per-task", required_argument, 0, 'c'}, {"constraint", required_argument, 0, 'C'}, {"dependency", required_argument, 0, 'd'}, @@ -615,7 +448,8 @@ static struct option long_options[] = { {"immediate", no_argument, 0, 'I'}, {"job-name", required_argument, 0, 'J'}, {"no-kill", no_argument, 0, 'k'}, - {"tasks", required_argument, 0, 'n'}, + {"distribution", required_argument, 0, 'm'}, + {"ntasks", required_argument, 0, 'n'}, {"nodes", required_argument, 0, 'N'}, {"output", required_argument, 0, 'o'}, {"overcommit", no_argument, 0, 'O'}, @@ -637,6 +471,8 @@ static struct option long_options[] = { {"mincores", required_argument, 0, LONG_OPT_MINCORES}, {"minthreads", required_argument, 0, LONG_OPT_MINTHREADS}, {"mem", required_argument, 0, LONG_OPT_MEM}, + {"job-mem", required_argument, 0, LONG_OPT_JOBMEM}, + {"hint", required_argument, 0, LONG_OPT_HINT}, {"tmp", required_argument, 0, LONG_OPT_TMP}, {"jobid", required_argument, 0, LONG_OPT_JOBID}, {"uid", required_argument, 0, LONG_OPT_UID}, @@ -648,20 +484,25 @@ static struct option long_options[] = { {"nice", optional_argument, 0, LONG_OPT_NICE}, {"no-requeue", no_argument, 0, LONG_OPT_NO_REQUEUE}, {"comment", required_argument, 0, LONG_OPT_COMMENT}, + {"sockets-per-node", required_argument, 0, LONG_OPT_SOCKETSPERNODE}, + {"cores-per-socket", required_argument, 0, LONG_OPT_CORESPERSOCKET}, + {"threads-per-core", required_argument, 0, LONG_OPT_THREADSPERCORE}, + {"ntasks-per-node", required_argument, 0, LONG_OPT_NTASKSPERNODE}, + {"ntasks-per-socket",required_argument, 0, LONG_OPT_NTASKSPERSOCKET}, + {"ntasks-per-core", required_argument, 0, LONG_OPT_NTASKSPERCORE}, {"blrts-image", required_argument, 0, LONG_OPT_BLRTS_IMAGE}, {"linux-image", required_argument, 0, LONG_OPT_LINUX_IMAGE}, {"mloader-image", required_argument, 0, LONG_OPT_MLOADER_IMAGE}, {"ramdisk-image", required_argument, 0, LONG_OPT_RAMDISK_IMAGE}, {"reboot", no_argument, 0, LONG_OPT_REBOOT}, - {"tasks-per-node", required_argument,0,LONG_OPT_TASKSPERNODE}, - {"ntasks-per-node", required_argument,0,LONG_OPT_TASKSPERNODE}, + {"tasks-per-node",required_argument, 0, LONG_OPT_NTASKSPERNODE}, {"wrap", required_argument, 0, LONG_OPT_WRAP}, {"get-user-env", no_argument, 0, LONG_OPT_GET_USER_ENV}, {NULL, 0, 0, 0} }; static char *opt_string = - "+a:bc:C:d:D:e:F:g:hHi:IJ:kn:N:o:Op:qR:st:uU:vVw:x:"; + "+a:bB:c:C:d:D:e:F:g:hHi:IJ:km:n:N:o:Op:qR:st:uU:vVw:x:"; /* @@ -712,7 +553,7 @@ char *process_options_first_pass(int argc, char **argv) opt.verbose++; break; case 'V': - _print_version(); + print_slurm_version(); exit(0); break; case LONG_OPT_WRAP: @@ -746,7 +587,7 @@ char *process_options_first_pass(int argc, char **argv) char *cmd = opt.script_argv[0]; int mode = R_OK; - if ((fullpath = _search_path(cmd, true, mode))) { + if ((fullpath = search_path(opt.cwd, cmd, true, mode))) { xfree(opt.script_argv[0]); opt.script_argv[0] = fullpath; } @@ -1010,6 +851,24 @@ static void _set_options(int argc, char **argv) /* Only here for Moab transition not suppose to do anything */ break; + case 'B': + opt.extra_set = verify_socket_core_thread_count( + optarg, + &opt.min_sockets_per_node, + &opt.max_sockets_per_node, + &opt.min_cores_per_socket, + &opt.max_cores_per_socket, + &opt.min_threads_per_core, + &opt.max_threads_per_core, + &opt.cpu_bind_type); + + + if (opt.extra_set == false) { + error("invalid resource allocation -B `%s'", + optarg); + exit(1); + } + break; case 'c': opt.cpus_set = true; opt.cpus_per_task = @@ -1045,7 +904,7 @@ static void _set_options(int argc, char **argv) } break; case 'g': - if (_verify_geometry(optarg, opt.geometry)) + if (verify_geometry(optarg, opt.geometry)) exit(1); break; case 'h': @@ -1071,6 +930,15 @@ static void _set_options(int argc, char **argv) case 'k': opt.no_kill = true; break; + case 'm': + opt.distribution = verify_dist_type(optarg, + &opt.plane_size); + if (opt.distribution == SLURM_DIST_UNKNOWN) { + error("distribution type `%s' " + "is not recognized", optarg); + exit(1); + } + break; case 'n': opt.nprocs_set = true; opt.nprocs = @@ -1078,9 +946,9 @@ static void _set_options(int argc, char **argv) break; case 'N': opt.nodes_set = - _verify_node_count(optarg, - &opt.min_nodes, - &opt.max_nodes); + verify_node_count(optarg, + &opt.min_nodes, + &opt.max_nodes); if (opt.nodes_set == false) { error("invalid node count `%s'", optarg); @@ -1125,7 +993,7 @@ static void _set_options(int argc, char **argv) opt.verbose++; break; case 'V': - _print_version(); + print_slurm_version(); exit(0); break; case 'w': @@ -1181,15 +1049,23 @@ static void _set_options(int argc, char **argv) } break; case LONG_OPT_MEM: - opt.realmem = (int) _to_bytes(optarg); + opt.realmem = (int) str_to_bytes(optarg); if (opt.realmem < 0) { error("invalid memory constraint %s", optarg); exit(1); } break; + case LONG_OPT_JOBMEM: + opt.jobmem = (int) str_to_bytes(optarg); + if (opt.jobmem < 0) { + error("invalid memory constraint %s", + optarg); + exit(1); + } + break; case LONG_OPT_TMP: - opt.tmpdisk = _to_bytes(optarg); + opt.tmpdisk = str_to_bytes(optarg); if (opt.tmpdisk < 0) { error("invalid tmp value %s", optarg); exit(1); @@ -1210,13 +1086,13 @@ static void _set_options(int argc, char **argv) fatal ("--gid=\"%s\" invalid", optarg); break; case LONG_OPT_CONNTYPE: - opt.conn_type = _verify_conn_type(optarg); + opt.conn_type = verify_conn_type(optarg); break; case LONG_OPT_BEGIN: opt.begin = parse_time(optarg); break; case LONG_OPT_MAIL_TYPE: - opt.mail_type |= _parse_mail_type(optarg); + opt.mail_type |= parse_mail_type(optarg); if (opt.mail_type == 0) fatal("--mail-type=%s invalid", optarg); break; @@ -1242,6 +1118,48 @@ static void _set_options(int argc, char **argv) xfree(opt.comment); opt.comment = xstrdup(optarg); break; + case LONG_OPT_SOCKETSPERNODE: + get_resource_arg_range( optarg, "sockets-per-node", + &opt.min_sockets_per_node, + &opt.max_sockets_per_node, + true ); + break; + case LONG_OPT_CORESPERSOCKET: + get_resource_arg_range( optarg, "cores-per-socket", + &opt.min_cores_per_socket, + &opt.max_cores_per_socket, + true); + break; + case LONG_OPT_THREADSPERCORE: + get_resource_arg_range( optarg, "threads-per-core", + &opt.min_threads_per_core, + &opt.max_threads_per_core, + true ); + break; + case LONG_OPT_HINT: + if (verify_hint(optarg, + &opt.min_sockets_per_node, + &opt.max_sockets_per_node, + &opt.min_cores_per_socket, + &opt.max_cores_per_socket, + &opt.min_threads_per_core, + &opt.max_threads_per_core, + &opt.cpu_bind_type)) { + exit(1); + } + break; + case LONG_OPT_NTASKSPERNODE: + opt.ntasks_per_node = _get_int(optarg, + "ntasks-per-node"); + break; + case LONG_OPT_NTASKSPERSOCKET: + opt.ntasks_per_socket = _get_int(optarg, + "ntasks-per-socket"); + break; + case LONG_OPT_NTASKSPERCORE: + opt.ntasks_per_core = _get_int(optarg, + "ntasks-per-core"); + break; case LONG_OPT_BLRTS_IMAGE: xfree(opt.blrtsimage); opt.blrtsimage = xstrdup(optarg); @@ -1261,9 +1179,6 @@ static void _set_options(int argc, char **argv) case LONG_OPT_REBOOT: opt.reboot = true; break; - case LONG_OPT_TASKSPERNODE: - opt.tasks_per_node = _get_int(optarg, "ntasks-per-node"); - break; case LONG_OPT_WRAP: /* handled in process_options_first_pass() */ break; @@ -1554,7 +1469,7 @@ static void _parse_pbs_resource_list(char *rl) */ temp[end] = '\0'; } - opt.tmpdisk = _to_bytes(temp); + opt.tmpdisk = str_to_bytes(temp); if (opt.tmpdisk < 0) { error("invalid tmp value %s", temp); exit(1); @@ -1578,7 +1493,7 @@ static void _parse_pbs_resource_list(char *rl) */ temp[end] = '\0'; } - opt.realmem = (int) _to_bytes(temp); + opt.realmem = (int) str_to_bytes(temp); if (opt.realmem < 0) { error("invalid memory constraint %s", temp); @@ -1663,7 +1578,7 @@ static bool _opt_verify(void) opt.mincpus = opt.cpus_per_task; if ((opt.job_name == NULL) && (opt.script_argc > 0)) - opt.job_name = _base_name(opt.script_argv[0]); + opt.job_name = base_name(opt.script_argv[0]); /* check for realistic arguments */ if (opt.nprocs <= 0) { @@ -1685,11 +1600,87 @@ static bool _opt_verify(void) verified = false; } + /* When CR with memory as a CR is enabled we need to assign + adequate value or check the value to opt.mem */ + if ((opt.realmem >= -1) && (opt.jobmem > 0)) { + if (opt.realmem == -1) { + opt.realmem = opt.jobmem; + } else if (opt.realmem < opt.jobmem) { + info("mem < job-mem - resizing mem to be equal to job-mem"); + opt.realmem = opt.jobmem; + } + } + + /* Check to see if user has specified enough resources to + * satisfy the plane distribution with the specified + * plane_size. + * if (n/plane_size < N) and ((N-1) * plane_size >= n) --> + * problem Simple check will not catch all the problem/invalid + * cases. + * The limitations of the plane distribution in the cons_res + * environment are more extensive and are documented in the + * SLURM reference guide. */ + if (opt.distribution == SLURM_DIST_PLANE && opt.plane_size) { + if ((opt.nprocs/opt.plane_size) < opt.min_nodes) { + if (((opt.min_nodes-1)*opt.plane_size) >= opt.nprocs) { +#if(0) + info("Too few processes ((n/plane_size) %d < N %d) " + "and ((N-1)*(plane_size) %d >= n %d)) ", + opt.nprocs/opt.plane_size, opt.min_nodes, + (opt.min_nodes-1)*opt.plane_size, opt.nprocs); +#endif + error("Too few processes for the requested " + "{plane,node} distribution"); + exit(1); + } + } + } + + /* bound max_threads/cores from ntasks_cores/sockets */ + if ((opt.max_threads_per_core <= 0) && + (opt.ntasks_per_core > 0)) { + opt.max_threads_per_core = opt.ntasks_per_core; + /* if cpu_bind_type doesn't already have a auto pref, + * choose the level based on the level of ntasks + */ + if (!(opt.cpu_bind_type & (CPU_BIND_TO_SOCKETS | + CPU_BIND_TO_CORES | + CPU_BIND_TO_THREADS))) { + opt.cpu_bind_type |= CPU_BIND_TO_CORES; + } + } + if ((opt.max_cores_per_socket <= 0) && + (opt.ntasks_per_socket > 0)) { + opt.max_cores_per_socket = opt.ntasks_per_socket; + /* if cpu_bind_type doesn't already have a auto pref, + * choose the level based on the level of ntasks + */ + if (!(opt.cpu_bind_type & (CPU_BIND_TO_SOCKETS | + CPU_BIND_TO_CORES | + CPU_BIND_TO_THREADS))) { + opt.cpu_bind_type |= CPU_BIND_TO_SOCKETS; + } + } + /* massage the numbers */ - if (opt.nodes_set && !opt.nprocs_set) { + if ((opt.nodes_set || opt.extra_set) && !opt.nprocs_set) { /* 1 proc / node default */ opt.nprocs = opt.min_nodes; + /* 1 proc / min_[socket * core * thread] default */ + if (opt.min_sockets_per_node > 0) { + opt.nprocs *= opt.min_sockets_per_node; + opt.nprocs_set = true; + } + if (opt.min_cores_per_socket > 0) { + opt.nprocs *= opt.min_cores_per_socket; + opt.nprocs_set = true; + } + if (opt.min_threads_per_core > 0) { + opt.nprocs *= opt.min_threads_per_core; + opt.nprocs_set = true; + } + } else if (opt.nodes_set && opt.nprocs_set) { /* @@ -1741,24 +1732,6 @@ static bool _opt_verify(void) return verified; } -static uint16_t _parse_mail_type(const char *arg) -{ - uint16_t rc; - - if (strcasecmp(arg, "BEGIN") == 0) - rc = MAIL_JOB_BEGIN; - else if (strcasecmp(arg, "END") == 0) - rc = MAIL_JOB_END; - else if (strcasecmp(arg, "FAIL") == 0) - rc = MAIL_JOB_FAIL; - else if (strcasecmp(arg, "ALL") == 0) - rc = MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL; - else - rc = 0; /* failure */ - - return rc; -} - static uint16_t _parse_pbs_mail_type(const char *arg) { uint16_t rc; @@ -1789,97 +1762,6 @@ static uint16_t _parse_pbs_mail_type(const char *arg) return rc; } -static char *_print_mail_type(const uint16_t type) -{ - if (type == 0) - return "NONE"; - - if (type == MAIL_JOB_BEGIN) - return "BEGIN"; - if (type == MAIL_JOB_END) - return "END"; - if (type == MAIL_JOB_FAIL) - return "FAIL"; - if (type == (MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL)) - return "ALL"; - - return "MULTIPLE"; -} - -static void -_freeF(void *data) -{ - xfree(data); -} - -static List -_create_path_list(void) -{ - List l = list_create(_freeF); - char *path = xstrdup(getenv("PATH")); - char *c, *lc; - - if (!path) { - error("Error in PATH environment variable"); - list_destroy(l); - return NULL; - } - - c = lc = path; - - while (*c != '\0') { - if (*c == ':') { - /* nullify and push token onto list */ - *c = '\0'; - if (lc != NULL && strlen(lc) > 0) - list_append(l, xstrdup(lc)); - lc = ++c; - } else - c++; - } - - if (strlen(lc) > 0) - list_append(l, xstrdup(lc)); - - xfree(path); - - return l; -} - -static char * -_search_path(char *cmd, bool check_current_dir, int access_mode) -{ - List l = _create_path_list(); - ListIterator i = NULL; - char *path, *fullpath = NULL; - - if ( (cmd[0] == '.' || cmd[0] == '/') - && (access(cmd, access_mode) == 0 ) ) { - if (cmd[0] == '.') - xstrfmtcat(fullpath, "%s/", opt.cwd); - xstrcat(fullpath, cmd); - goto done; - } - - if (check_current_dir) - list_prepend(l, xstrdup(opt.cwd)); - - i = list_iterator_create(l); - while ((path = list_next(i))) { - xstrfmtcat(fullpath, "%s/%s", path, cmd); - - if (access(fullpath, access_mode) == 0) - goto done; - - xfree(fullpath); - fullpath = NULL; - } - done: - list_destroy(l); - return fullpath; -} - - /* helper function for printing options * * warning: returns pointer to memory allocated on the stack. @@ -1891,9 +1773,21 @@ static char *print_constraints() if (opt.mincpus > 0) xstrfmtcat(buf, "mincpus=%d ", opt.mincpus); + if (opt.minsockets > 0) + xstrfmtcat(buf, "minsockets=%d ", opt.minsockets); + + if (opt.mincores > 0) + xstrfmtcat(buf, "mincores=%d ", opt.mincores); + + if (opt.minthreads > 0) + xstrfmtcat(buf, "minthreads=%d ", opt.minthreads); + if (opt.realmem > 0) xstrfmtcat(buf, "mem=%dM ", opt.realmem); + if (opt.jobmem > 0) + xstrfmtcat(buf, "job-mem=%dM ", opt.jobmem); + if (opt.tmpdisk > 0) xstrfmtcat(buf, "tmp=%ld ", opt.tmpdisk); @@ -1912,40 +1806,6 @@ static char *print_constraints() return buf; } -static char * -print_commandline() -{ - int i; - char buf[256]; - - buf[0] = '\0'; - for (i = 0; i < opt.script_argc; i++) - snprintf(buf, 256, "%s", opt.script_argv[i]); - return xstrdup(buf); -} - -static char * -print_geometry() -{ - int i; - char buf[32], *rc = NULL; - - if ((SYSTEM_DIMENSIONS == 0) - || (opt.geometry[0] == (uint16_t)NO_VAL)) - return NULL; - - for (i=0; i<SYSTEM_DIMENSIONS; i++) { - if (i > 0) - snprintf(buf, sizeof(buf), "x%u", opt.geometry[i]); - else - snprintf(buf, sizeof(buf), "%u", opt.geometry[i]); - xstrcat(rc, buf); - } - - return rc; -} - - /* * Get a decimal integer from arg. * @@ -2022,6 +1882,9 @@ static void _opt_list() info("partition : %s", opt.partition == NULL ? "default" : opt.partition); info("job name : `%s'", opt.job_name); + info("distribution : %s", format_task_dist_states(opt.distribution)); + if(opt.distribution == SLURM_DIST_PLANE) + info("plane size : %u", opt.plane_size); info("verbose : %d", opt.verbose); info("immediate : %s", tf_(opt.immediate)); info("no-requeue : %s", tf_(opt.no_requeue)); @@ -2043,7 +1906,7 @@ static void _opt_list() xfree(str); if (opt.conn_type != (uint16_t) NO_VAL) info("conn_type : %u", opt.conn_type); - str = print_geometry(); + str = print_geometry(opt.geometry); info("geometry : %s", str); xfree(str); info("reboot : %s", opt.reboot ? "no" : "yes"); @@ -2063,10 +1926,19 @@ static void _opt_list() slurm_make_time_str(&opt.begin, time_str, sizeof(time_str)); info("begin : %s", time_str); } - info("mail_type : %s", _print_mail_type(opt.mail_type)); + info("mail_type : %s", print_mail_type(opt.mail_type)); info("mail_user : %s", opt.mail_user); - info("tasks-per-node : %d", opt.tasks_per_node); - str = print_commandline(); + info("sockets-per-node : %d - %d", opt.min_sockets_per_node, + opt.max_sockets_per_node); + info("cores-per-socket : %d - %d", opt.min_cores_per_socket, + opt.max_cores_per_socket); + info("threads-per-core : %d - %d", opt.min_threads_per_core, + opt.max_threads_per_core); + info("ntasks-per-node : %d", opt.ntasks_per_node); + info("ntasks-per-socket : %d", opt.ntasks_per_socket); + info("ntasks-per-core : %d", opt.ntasks_per_core); + info("plane_size : %u", opt.plane_size); + str = print_commandline(opt.script_argc, opt.script_argv); info("remote command : `%s'", str); xfree(str); @@ -2097,6 +1969,8 @@ static void _usage(void) static void _help(void) { + slurm_ctl_conf_t *conf; + printf ( "Usage: sbatch [OPTIONS...] executable [args...]\n" "\n" @@ -2149,7 +2023,30 @@ static void _help(void) "Consumable resources related options:\n" " --exclusive allocate nodes in exclusive mode when\n" " cpu consumable resource is enabled\n" +" --job-mem=MB maximum amount of real memory per node\n" +" required by the job.\n" +" --mem >= --job-mem if --mem is specified.\n" "\n" +"Affinity/Multi-core options: (when the task/affinity plugin is enabled)\n" +" -B --extra-node-info=S[:C[:T]] Expands to:\n" +" --sockets-per-node=S number of sockets per node to allocate\n" +" --cores-per-socket=C number of cores per socket to allocate\n" +" --threads-per-core=T number of threads per core to allocate\n" +" each field can be 'min[-max]' or wildcard '*'\n" +" total cpus requested = (N x S x C x T)\n" +"\n" +" --ntasks-per-socket=n number of tasks to invoke on each socket\n" +" --ntasks-per-core=n number of tasks to invoke on each core\n"); + conf = slurm_conf_lock(); + if (conf->task_plugin != NULL + && strcasecmp(conf->task_plugin, "task/affinity") == 0) { + printf( +" --hint= Bind tasks according to application hints\n" +" (see \"--hint=help\" for options)\n"); + } + slurm_conf_unlock(); + + printf("\n" #ifdef HAVE_BG /* Blue gene specific options */ "Blue Gene related options:\n" " -g, --geometry=XxYxZ geometry constraints of the job\n" @@ -2161,8 +2058,8 @@ static void _help(void) " --linux-image=path path to linux image for bluegene block. Default if not set\n" " --mloader-image=path path to mloader image for bluegene block. Default if not set\n" " --ramdisk-image=path path to ramdisk image for bluegene block. Default if not set\n" -"\n" #endif +"\n" "Help options:\n" " -h, --help show this help message\n" " -u, --usage display brief usage message\n" diff --git a/src/sbatch/opt.h b/src/sbatch/opt.h index 1b10d7c5391..330ecf7d07c 100644 --- a/src/sbatch/opt.h +++ b/src/sbatch/opt.h @@ -42,6 +42,7 @@ #define MAX_USERNAME 9 + typedef struct sbatch_options { char *progname; /* argv[0] of this program or */ @@ -63,9 +64,25 @@ typedef struct sbatch_options { int min_nodes; /* --nodes=n, -N n */ int max_nodes; /* --nodes=x-n, -N x-n */ bool nodes_set; /* true if nodes explicitly set */ + int min_sockets_per_node; /* --sockets-per-node=n */ + int max_sockets_per_node; /* --sockets-per-node=x-n */ + int min_cores_per_socket; /* --cores-per-socket=n */ + int max_cores_per_socket; /* --cores-per-socket=x-n */ + int min_threads_per_core; /* --threads-per-core=n */ + int max_threads_per_core; /* --threads-per-core=x-n */ + int ntasks_per_node; /* --ntasks-per-node=n */ + int ntasks_per_socket; /* --ntasks-per-socket=n */ + int ntasks_per_core; /* --ntasks-per-core=n */ + cpu_bind_type_t cpu_bind_type; /* --cpu_bind= */ + bool extra_set; /* true if extra node info explicitly set */ int time_limit; /* --time, -t (int minutes) */ char *time_limit_str; /* --time, -t (string) */ char *partition; /* --partition=n, -p n */ + enum task_dist_states + distribution; /* --distribution=, -m dist */ + uint32_t plane_size; /* lllp distribution -> plane_size for + * when -m plane=<# of lllp per + * plane> */ char *job_name; /* --job-name=, -J name */ unsigned int jobid; /* --jobid=jobid */ bool jobid_set; /* true of jobid explicitly set */ @@ -91,9 +108,9 @@ typedef struct sbatch_options { int minsockets; /* --minsockets=n */ int mincores; /* --mincores=n */ int minthreads; /* --minthreads=n */ + int jobmem; /* --job-mem=n */ int realmem; /* --mem=n */ long tmpdisk; /* --tmp=n */ - int tasks_per_node; /* --tasks-per-node=n */ char *constraints; /* --constraints=, -C constraint*/ bool contiguous; /* --contiguous */ char *nodelist; /* --nodelist=node1,node2,... */ diff --git a/src/sbatch/sbatch.c b/src/sbatch/sbatch.c index 165f108625d..c0fe4f113ef 100644 --- a/src/sbatch/sbatch.c +++ b/src/sbatch/sbatch.c @@ -128,6 +128,9 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->user_id = opt.uid; desc->group_id = opt.gid; desc->dependency = opt.dependency; + desc->task_dist = opt.distribution; + if (opt.plane_size != NO_VAL) + desc->plane_size = opt.plane_size; if (opt.nice) desc->nice = NICE_OFFSET + opt.nice; desc->mail_type = opt.mail_type; @@ -164,6 +167,7 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) if (opt.ramdiskimage) desc->ramdiskimage = xstrdup(opt.ramdiskimage); + /* job constraints */ if (opt.mincpus > -1) desc->job_min_procs = opt.mincpus; if (opt.minsockets > -1) @@ -172,6 +176,8 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->job_min_cores = opt.mincores; if (opt.minthreads > -1) desc->job_min_threads = opt.minthreads; + if (opt.jobmem > -1) + desc->job_max_memory = opt.jobmem; if (opt.realmem > -1) desc->job_min_memory = opt.realmem; if (opt.tmpdisk > -1) @@ -181,12 +187,29 @@ static int fill_job_desc_from_opts(job_desc_msg_t *desc) desc->overcommit = opt.overcommit; } else desc->num_procs = opt.nprocs * opt.cpus_per_task; - if (opt.tasks_per_node > -1) - desc->ntasks_per_node = opt.tasks_per_node; if (opt.nprocs_set) desc->num_tasks = opt.nprocs; if (opt.cpus_set) desc->cpus_per_task = opt.cpus_per_task; + if (opt.ntasks_per_socket > -1) + desc->ntasks_per_socket = opt.ntasks_per_socket; + if (opt.ntasks_per_core > -1) + desc->ntasks_per_core = opt.ntasks_per_core; + + /* node constraints */ + if (opt.min_sockets_per_node > -1) + desc->min_sockets = opt.min_sockets_per_node; + if (opt.max_sockets_per_node > -1) + desc->max_sockets = opt.max_sockets_per_node; + if (opt.min_cores_per_socket > -1) + desc->min_cores = opt.min_cores_per_socket; + if (opt.max_cores_per_socket > -1) + desc->max_cores = opt.max_cores_per_socket; + if (opt.min_threads_per_core > -1) + desc->min_threads = opt.min_threads_per_core; + if (opt.max_threads_per_core > -1) + desc->max_threads = opt.max_threads_per_core; + if (opt.no_kill) desc->kill_on_node_fail = 0; if (opt.time_limit != NO_VAL) diff --git a/src/srun/opt.c b/src/srun/opt.c index e0188bef21b..e5541dce4a1 100644 --- a/src/srun/opt.c +++ b/src/srun/opt.c @@ -72,6 +72,7 @@ #include "src/common/list.h" #include "src/common/log.h" #include "src/common/parse_time.h" +#include "src/common/proc_args.h" #include "src/common/slurm_protocol_api.h" #include "src/common/slurm_protocol_interface.h" #include "src/common/uid.h" @@ -169,15 +170,9 @@ opt_t opt; typedef struct env_vars env_vars_t; -/* return command name from its full path name */ -static char * _base_name(char* command); - -static List _create_path_list(void); /* Get a decimal integer from arg */ static int _get_int(const char *arg, const char *what, bool positive); -static bool _get_resource_range(const char *arg, const char *what, - int *min, int *max, bool isFatal); static void _help(void); @@ -198,39 +193,16 @@ static void _opt_list(void); /* verify options sanity */ static bool _opt_verify(void); -static void _print_version(void); - static void _process_env_var(env_vars_t *e, const char *val); -static uint16_t _parse_mail_type(const char *arg); -static char *_print_mail_type(const uint16_t type); - -/* search PATH for command returns full path */ -static char *_search_path(char *, bool, int); - -static long _to_bytes(const char *arg); - static bool _under_parallel_debugger(void); static void _usage(void); static bool _valid_node_list(char **node_list_pptr); -static task_dist_states_t _verify_dist_type(const char *arg, uint32_t *psize); -static bool _verify_socket_core_thread_count(const char *arg, - int *min_sockets, int *max_sockets, - int *min_cores, int *max_cores, - int *min_threads, int *max_threads, - cpu_bind_type_t *cpu_bind_type); -static bool _verify_hint(const char *arg, - int *min_sockets, int *max_sockets, - int *min_cores, int *max_cores, - int *min_threads, int *max_threads, - cpu_bind_type_t *cpu_bind_type); static int _verify_cpu_bind(const char *arg, char **cpu_bind, cpu_bind_type_t *flags); -static int _verify_geometry(const char *arg, uint16_t *geometry); static int _verify_mem_bind(const char *arg, char **mem_bind, mem_bind_type_t *flags); -static int _verify_conn_type(const char *arg); /*---[ end forward declarations of static functions ]---------------------*/ @@ -255,11 +227,6 @@ int initialize_and_process_args(int argc, char *argv[]) } -static void _print_version(void) -{ - printf("%s %s\n", PACKAGE, SLURM_VERSION); -} - /* * If the node list supplied is a file name, translate that into * a list of nodes, we orphan the data pointed to @@ -297,117 +264,6 @@ static bool _valid_node_list(char **node_list_pptr) return true; } -/* - * verify that a distribution type in arg is of a known form - * returns the task_dist_states, or -1 if state is unknown - */ -static task_dist_states_t _verify_dist_type(const char *arg, - uint32_t *plane_size) -{ - int len = strlen(arg); - char *dist_str = NULL; - task_dist_states_t result = SLURM_DIST_UNKNOWN; - bool lllp_dist = false, plane_dist = false; - - dist_str = strchr(arg,':'); - if (dist_str != NULL) { - /* -m cyclic|block:cyclic|block */ - lllp_dist = true; - } else { - /* -m plane=<plane_size> */ - dist_str = strchr(arg,'='); - if(dist_str != NULL) { - *plane_size=atoi(dist_str+1); - len = dist_str-arg; - plane_dist = true; - } - } - - if (lllp_dist) { - if (strcasecmp(arg, "cyclic:cyclic") == 0) { - result = SLURM_DIST_CYCLIC_CYCLIC; - } else if (strcasecmp(arg, "cyclic:block") == 0) { - result = SLURM_DIST_CYCLIC_BLOCK; - } else if (strcasecmp(arg, "block:block") == 0) { - result = SLURM_DIST_BLOCK_BLOCK; - } else if (strcasecmp(arg, "block:cyclic") == 0) { - result = SLURM_DIST_BLOCK_CYCLIC; - } - } else if (plane_dist) { - if (strncasecmp(arg, "plane", len) == 0) { - result = SLURM_DIST_PLANE; - } - } else { - if (strncasecmp(arg, "cyclic", len) == 0) { - result = SLURM_DIST_CYCLIC; - } else if (strncasecmp(arg, "block", len) == 0) { - result = SLURM_DIST_BLOCK; - } else if ((strncasecmp(arg, "arbitrary", len) == 0) || - (strncasecmp(arg, "hostfile", len) == 0)) { - result = SLURM_DIST_ARBITRARY; - } - } - - return result; -} - -/* - * verify that a connection type in arg is of known form - * returns the connection_type or -1 if not recognized - */ -static int _verify_conn_type(const char *arg) -{ - int len = strlen(arg); - - if (!strncasecmp(arg, "MESH", len)) - return SELECT_MESH; - else if (!strncasecmp(arg, "TORUS", len)) - return SELECT_TORUS; - else if (!strncasecmp(arg, "NAV", len)) - return SELECT_NAV; - - error("invalid --conn-type argument %s ignored.", arg); - return -1; -} - -/* - * verify geometry arguments, must have proper count - * returns -1 on error, 0 otherwise - */ -static int _verify_geometry(const char *arg, uint16_t *geometry) -{ - char* token, *delimiter = ",x", *next_ptr; - int i, rc = 0; - char* geometry_tmp = xstrdup(arg); - char* original_ptr = geometry_tmp; - - token = strtok_r(geometry_tmp, delimiter, &next_ptr); - for (i=0; i<SYSTEM_DIMENSIONS; i++) { - if (token == NULL) { - error("insufficient dimensions in --geometry"); - rc = -1; - break; - } - geometry[i] = (uint16_t)atoi(token); - if (geometry[i] == 0 || geometry[i] == (uint16_t)NO_VAL) { - error("invalid --geometry argument"); - rc = -1; - break; - } - geometry_tmp = next_ptr; - token = strtok_r(geometry_tmp, delimiter, &next_ptr); - } - if (token != NULL) { - error("too many dimensions in --geometry"); - rc = -1; - } - - if (original_ptr) - xfree(original_ptr); - - return rc; -} - /* * _isvalue * returns 1 is the argument appears to be a value, 0 otherwise @@ -675,203 +531,6 @@ static int _verify_mem_bind(const char *arg, char **mem_bind, return 0; } -/* - * verify that a resource counts in arg are of a known form X, X:X, X:X:X, or - * X:X:X:X, where X is defined as either (count, min-max, or '*') - * RET true if valid - */ -static bool -_verify_socket_core_thread_count(const char *arg, - int *min_sockets, int *max_sockets, - int *min_cores, int *max_cores, - int *min_threads, int *max_threads, - cpu_bind_type_t *cpu_bind_type) -{ - bool tmp_val,ret_val; - int i,j; - const char *cur_ptr = arg; - char buf[3][48]; /* each can hold INT64_MAX - INT64_MAX */ - buf[0][0] = '\0'; - buf[1][0] = '\0'; - buf[2][0] = '\0'; - - for (j=0;j<3;j++) { - for (i=0;i<47;i++) { - if (*cur_ptr == '\0' || *cur_ptr ==':') break; - buf[j][i] = *cur_ptr++; - } - if (*cur_ptr == '\0') break; - xassert(*cur_ptr == ':'); - buf[j][i] = '\0'; - cur_ptr++; - } - /* if cpu_bind_type doesn't already have a auto preference, choose - * the level based on the level of the -E specification - */ - if (!(*cpu_bind_type & (CPU_BIND_TO_SOCKETS | - CPU_BIND_TO_CORES | - CPU_BIND_TO_THREADS))) { - if (j == 0) { - *cpu_bind_type |= CPU_BIND_TO_SOCKETS; - } else if (j == 1) { - *cpu_bind_type |= CPU_BIND_TO_CORES; - } else if (j == 2) { - *cpu_bind_type |= CPU_BIND_TO_THREADS; - } - } - buf[j][i] = '\0'; - - ret_val = true; - tmp_val = _get_resource_range(&buf[0][0], "first arg of -B", - min_sockets, max_sockets, true); - ret_val = ret_val && tmp_val; - tmp_val = _get_resource_range(&buf[1][0], "second arg of -B", - min_cores, max_cores, true); - ret_val = ret_val && tmp_val; - tmp_val = _get_resource_range(&buf[2][0], "third arg of -B", - min_threads, max_threads, true); - ret_val = ret_val && tmp_val; - - return ret_val; -} - -/* - * verify that a hint is valid and convert it into the implied settings - * RET true if valid - */ -static bool -_verify_hint(const char *arg, - int *min_sockets, int *max_sockets, - int *min_cores, int *max_cores, - int *min_threads, int *max_threads, - cpu_bind_type_t *cpu_bind_type) -{ - char *buf, *p, *tok; - if (!arg) { - return true; - } - - 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] == ',') && (!isdigit(p[1]))) - p[0] = ';'; - p++; - } - - p = buf; - while ((tok = strsep(&p, ";"))) { - if (strcasecmp(tok, "help") == 0) { - printf( -"Application hint options:\n" -" --hint= Bind tasks according to application hints\n" -" compute_bound use all cores in each physical CPU\n" -" memory_bound use only one core in each physical CPU\n" -" [no]multithread [don't] use extra threads with in-core multi-threading\n" -" help show this help message\n"); - return 1; - } else if (strcasecmp(tok, "compute_bound") == 0) { - *min_sockets = 1; - *max_sockets = INT_MAX; - *min_cores = 1; - *max_cores = INT_MAX; - *cpu_bind_type |= CPU_BIND_TO_CORES; - } else if (strcasecmp(tok, "memory_bound") == 0) { - *min_cores = 1; - *max_cores = 1; - *cpu_bind_type |= CPU_BIND_TO_CORES; - } else if (strcasecmp(tok, "multithread") == 0) { - *min_threads = 1; - *max_threads = INT_MAX; - *cpu_bind_type |= CPU_BIND_TO_THREADS; - } else if (strcasecmp(tok, "nomultithread") == 0) { - *min_threads = 1; - *max_threads = 1; - *cpu_bind_type |= CPU_BIND_TO_THREADS; - } else { - error("unrecognized --hint argument \"%s\", see --hint=help", tok); - xfree(buf); - return 1; - } - } - - xfree(buf); - return 0; -} - -/* return command name from its full path name */ -static char * _base_name(char* command) -{ - char *char_ptr, *name; - int i; - - if (command == NULL) - return NULL; - - char_ptr = strrchr(command, (int)'/'); - if (char_ptr == NULL) - char_ptr = command; - else - char_ptr++; - - i = strlen(char_ptr); - name = xmalloc(i+1); - strcpy(name, char_ptr); - return name; -} - -/* - * _to_bytes(): verify that arg is numeric with optional "G" or "M" at end - * if "G" or "M" is there, multiply by proper power of 2 and return - * number in bytes - */ -static long _to_bytes(const char *arg) -{ - char *buf; - char *endptr; - int end; - int multiplier = 1; - long result; - - buf = xstrdup(arg); - - end = strlen(buf) - 1; - - if (isdigit(buf[end])) { - result = strtol(buf, &endptr, 10); - - if (*endptr != '\0') - result = -result; - - } else { - - switch (toupper(buf[end])) { - - case 'G': - multiplier = 1024; - break; - - case 'M': - /* do nothing */ - break; - - default: - multiplier = -1; - } - - buf[end] = '\0'; - - result = multiplier * strtol(buf, &endptr, 10); - - if (*endptr != '\0') - result = -result; - } - - return result; -} - /* * print error message to stderr with opt.progname prepended */ @@ -1156,7 +815,7 @@ _process_env_var(env_vars_t *e, const char *val) case OPT_DISTRIB: if (strcmp(val, "unknown") == 0) break; /* ignore it, passed from salloc */ - dt = _verify_dist_type(val, &opt.plane_size); + dt = verify_dist_type(val, &opt.plane_size); if (dt == SLURM_DIST_UNKNOWN) { error("\"%s=%s\" -- invalid distribution type. " "ignoring...", e->var, val); @@ -1177,9 +836,9 @@ _process_env_var(env_vars_t *e, const char *val) break; case OPT_NODES: - opt.nodes_set = _get_resource_range( val ,"OPT_NODES", - &opt.min_nodes, - &opt.max_nodes, false); + opt.nodes_set = get_resource_arg_range( val ,"OPT_NODES", + &opt.min_nodes, + &opt.max_nodes, false); if (opt.nodes_set == false) { error("\"%s=%s\" -- invalid node count. ignoring...", e->var, val); @@ -1200,7 +859,7 @@ _process_env_var(env_vars_t *e, const char *val) break; case OPT_CONN_TYPE: - opt.conn_type = _verify_conn_type(val); + opt.conn_type = verify_conn_type(val); break; case OPT_NO_ROTATE: @@ -1208,7 +867,7 @@ _process_env_var(env_vars_t *e, const char *val) break; case OPT_GEOMETRY: - if (_verify_geometry(val, opt.geometry)) { + if (verify_geometry(val, opt.geometry)) { error("\"%s=%s\" -- invalid geometry, ignoring...", e->var, val); } @@ -1253,70 +912,6 @@ _get_int(const char *arg, const char *what, bool positive) return (int) result; } -/* - * get either 1 or 2 integers for a resource count in the form of either - * (count, min-max, or '*') - * A partial error message is passed in via the 'what' param. - * RET true if valid - */ -static bool -_get_resource_range(const char *arg, const char *what, int* min, int *max, - bool isFatal) -{ - char *p; - long int result; - - if (*arg == '\0') return true; - - /* wildcard meaning every possible value in range */ - if (*arg == '*' ) { - *min = 1; - *max = INT_MAX; - return true; - } - - result = strtol(arg, &p, 10); - if (*p == 'k' || *p == 'K') { - result *= 1024; - p++; - } - - if (((*p != '\0')&&(*p != '-')) || (result <= 0L)) { - error ("Invalid numeric value \"%s\" for %s.", arg, what); - if (isFatal) exit(1); - return false; - } else if (result > INT_MAX) { - error ("Numeric argument (%ld) to big for %s.", result, what); - if (isFatal) exit(1); - return false; - } - - *min = (int) result; - - if (*p == '\0') return true; - if (*p == '-') p++; - - result = strtol(p, &p, 10); - if (*p == 'k' || *p == 'K') { - result *= 1024; - p++; - } - - if (((*p != '\0')&&(*p != '-')) || (result <= 0L)) { - error ("Invalid numeric value \"%s\" for %s.", arg, what); - if (isFatal) exit(1); - return false; - } else if (result > INT_MAX) { - error ("Numeric argument (%ld) to big for %s.", result, what); - if (isFatal) exit(1); - return false; - } - - *max = (int) result; - - return true; -} - static void set_options(const int argc, char **argv) { int opt_char, option_index = 0; @@ -1407,6 +1002,7 @@ static void set_options(const int argc, char **argv) {"ntasks-per-node", required_argument, 0, LONG_OPT_NTASKSPERNODE}, {"ntasks-per-socket",required_argument, 0, LONG_OPT_NTASKSPERSOCKET}, {"ntasks-per-core", required_argument, 0, LONG_OPT_NTASKSPERCORE}, + {"tasks-per-node", required_argument, 0, LONG_OPT_NTASKSPERNODE}, {"blrts-image", required_argument, 0, LONG_OPT_BLRTS_IMAGE}, {"linux-image", required_argument, 0, LONG_OPT_LINUX_IMAGE}, {"mloader-image", required_argument, 0, LONG_OPT_MLOADER_IMAGE}, @@ -1454,7 +1050,7 @@ static void set_options(const int argc, char **argv) "\"srun -b/--batch\"."); exit(1); case (int)'B': - opt.extra_set = _verify_socket_core_thread_count( + opt.extra_set = verify_socket_core_thread_count( optarg, &opt.min_sockets_per_node, &opt.max_sockets_per_node, @@ -1499,7 +1095,7 @@ static void set_options(const int argc, char **argv) opt.efname = xstrdup(optarg); break; case (int)'g': - if (_verify_geometry(optarg, opt.geometry)) + if (verify_geometry(optarg, opt.geometry)) exit(1); break; case (int)'H': @@ -1535,7 +1131,7 @@ static void set_options(const int argc, char **argv) opt.labelio = true; break; case (int)'m': - opt.distribution = _verify_dist_type(optarg, + opt.distribution = verify_dist_type(optarg, &opt.plane_size); if (opt.distribution == SLURM_DIST_UNKNOWN) { error("distribution type `%s' " @@ -1550,10 +1146,10 @@ static void set_options(const int argc, char **argv) break; case (int)'N': opt.nodes_set = - _get_resource_range(optarg, - "requested node count", - &opt.min_nodes, - &opt.max_nodes, true); + get_resource_arg_range( optarg, + "requested node count", + &opt.min_nodes, + &opt.max_nodes, true ); if (opt.nodes_set == false) { error("invalid resource allocation -N `%s'", @@ -1616,7 +1212,7 @@ static void set_options(const int argc, char **argv) _verbose++; break; case (int)'V': - _print_version(); + print_slurm_version(); exit(0); break; case (int)'w': @@ -1685,7 +1281,7 @@ static void set_options(const int argc, char **argv) true); break; case LONG_OPT_MEM: - opt.job_min_memory = (int) _to_bytes(optarg); + opt.job_min_memory = (int) str_to_bytes(optarg); if (opt.job_min_memory < 0) { error("invalid memory constraint %s", optarg); @@ -1693,7 +1289,7 @@ static void set_options(const int argc, char **argv) } break; case LONG_OPT_JOBMEM: - opt.job_max_memory = (int) _to_bytes(optarg); + opt.job_max_memory = (int) str_to_bytes(optarg); if (opt.job_max_memory < 0) { error("invalid memory constraint %s", optarg); @@ -1709,7 +1305,7 @@ static void set_options(const int argc, char **argv) } break; case LONG_OPT_TMP: - opt.job_min_tmp_disk = _to_bytes(optarg); + opt.job_min_tmp_disk = str_to_bytes(optarg); if (opt.job_min_tmp_disk < 0) { error("invalid tmp value %s", optarg); exit(1); @@ -1759,7 +1355,7 @@ static void set_options(const int argc, char **argv) _usage(); exit(0); case LONG_OPT_CONNTYPE: - opt.conn_type = _verify_conn_type(optarg); + opt.conn_type = verify_conn_type(optarg); break; case LONG_OPT_TEST_ONLY: opt.test_only = true; @@ -1788,7 +1384,7 @@ static void set_options(const int argc, char **argv) opt.begin = parse_time(optarg); break; case LONG_OPT_MAIL_TYPE: - opt.mail_type |= _parse_mail_type(optarg); + opt.mail_type |= parse_mail_type(optarg); if (opt.mail_type == 0) fatal("--mail-type=%s invalid", optarg); break; @@ -1827,22 +1423,25 @@ static void set_options(const int argc, char **argv) opt.comment = xstrdup(optarg); break; case LONG_OPT_SOCKETSPERNODE: - _get_resource_range( optarg, "sockets-per-node", - &opt.min_sockets_per_node, - &opt.max_sockets_per_node, true ); + get_resource_arg_range( optarg, "sockets-per-node", + &opt.min_sockets_per_node, + &opt.max_sockets_per_node, + true ); break; case LONG_OPT_CORESPERSOCKET: - _get_resource_range( optarg, "cores-per-socket", - &opt.min_cores_per_socket, - &opt.max_cores_per_socket, true); + get_resource_arg_range( optarg, "cores-per-socket", + &opt.min_cores_per_socket, + &opt.max_cores_per_socket, + true); break; case LONG_OPT_THREADSPERCORE: - _get_resource_range( optarg, "threads-per-core", - &opt.min_threads_per_core, - &opt.max_threads_per_core, true ); + get_resource_arg_range( optarg, "threads-per-core", + &opt.min_threads_per_core, + &opt.max_threads_per_core, + true ); break; case LONG_OPT_HINT: - if (_verify_hint(optarg, + if (verify_hint(optarg, &opt.min_sockets_per_node, &opt.max_sockets_per_node, &opt.min_cores_per_socket, @@ -2031,7 +1630,7 @@ static void _opt_args(int argc, char **argv) else if (opt.argc > 0) { char *fullpath; - if ((fullpath = _search_path(opt.argv[0], false, R_OK|X_OK))) { + if ((fullpath = search_path(opt.cwd, opt.argv[0], false, R_OK|X_OK))) { xfree(opt.argv[0]); opt.argv[0] = fullpath; } @@ -2088,7 +1687,7 @@ static bool _opt_verify(void) opt.job_min_cpus = opt.cpus_per_task; if ((opt.job_name == NULL) && (opt.argc > 0)) - opt.job_name = _base_name(opt.argv[0]); + opt.job_name = base_name(opt.argv[0]); if(!opt.nodelist) { if((opt.nodelist = xstrdup(getenv("SLURM_HOSTFILE")))) { @@ -2262,10 +1861,10 @@ static bool _opt_verify(void) } else if (opt.nodes_set && opt.nprocs_set) { /* - * Make sure in a non allocate situation that - * the number of max_nodes is <= number of tasks + * Make sure that the number of + * max_nodes is <= number of tasks */ - if (!opt.allocate && opt.nprocs < opt.max_nodes) + if (opt.nprocs < opt.max_nodes) opt.max_nodes = opt.nprocs; /* @@ -2366,114 +1965,6 @@ static bool _opt_verify(void) return verified; } -static uint16_t _parse_mail_type(const char *arg) -{ - uint16_t rc; - - if (strcasecmp(arg, "BEGIN") == 0) - rc = MAIL_JOB_BEGIN; - else if (strcasecmp(arg, "END") == 0) - rc = MAIL_JOB_END; - else if (strcasecmp(arg, "FAIL") == 0) - rc = MAIL_JOB_FAIL; - else if (strcasecmp(arg, "ALL") == 0) - rc = MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL; - else - rc = 0; /* failure */ - - return rc; -} - -static char *_print_mail_type(const uint16_t type) -{ - if (type == 0) - return "NONE"; - - if (type == MAIL_JOB_BEGIN) - return "BEGIN"; - if (type == MAIL_JOB_END) - return "END"; - if (type == MAIL_JOB_FAIL) - return "FAIL"; - if (type == (MAIL_JOB_BEGIN | MAIL_JOB_END | MAIL_JOB_FAIL)) - return "ALL"; - - return "MULTIPLE"; -} - -static void -_freeF(void *data) -{ - xfree(data); -} - -static List -_create_path_list(void) -{ - List l = list_create(_freeF); - char *path = xstrdup(getenv("PATH")); - char *c, *lc; - - if (!path) { - verbose("No PATH environment variable"); - return l; - } - - c = lc = path; - - while (*c != '\0') { - if (*c == ':') { - /* nullify and push token onto list */ - *c = '\0'; - if (lc != NULL && strlen(lc) > 0) - list_append(l, xstrdup(lc)); - lc = ++c; - } else - c++; - } - - if (strlen(lc) > 0) - list_append(l, xstrdup(lc)); - - xfree(path); - - return l; -} - -static char * -_search_path(char *cmd, bool check_current_dir, int access_mode) -{ - List l = _create_path_list(); - ListIterator i = NULL; - char *path, *fullpath = NULL; - - if ( (cmd[0] == '.' || cmd[0] == '/') - && (access(cmd, access_mode) == 0 ) ) { - if (cmd[0] == '.') - xstrfmtcat(fullpath, "%s/", opt.cwd); - xstrcat(fullpath, cmd); - goto done; - } - - if (check_current_dir) - list_prepend(l, xstrdup(opt.cwd)); - - i = list_iterator_create(l); - while ((path = list_next(i))) { - xstrfmtcat(fullpath, "%s/%s", path, cmd); - - if (access(fullpath, access_mode) == 0) - goto done; - - xfree(fullpath); - fullpath = NULL; - } -done: - list_destroy(l); - return fullpath; -} - - /* helper function for printing options * * warning: returns pointer to memory allocated on the stack. @@ -2518,39 +2009,6 @@ static char *print_constraints() return buf; } -static char * -print_commandline() -{ - int i; - char buf[256]; - - buf[0] = '\0'; - for (i = 0; i < opt.argc; i++) - snprintf(buf, 256, "%s", opt.argv[i]); - return xstrdup(buf); -} - -static char * -print_geometry() -{ - int i; - char buf[32], *rc = NULL; - - if ((SYSTEM_DIMENSIONS == 0) - || (opt.geometry[0] == (uint16_t)NO_VAL)) - return NULL; - - for (i=0; i<SYSTEM_DIMENSIONS; i++) { - if (i > 0) - snprintf(buf, sizeof(buf), "x%u", opt.geometry[i]); - else - snprintf(buf, sizeof(buf), "%u", opt.geometry[i]); - xstrcat(rc, buf); - } - - return rc; -} - #define tf_(b) (b == true) ? "true" : "false" static void _opt_list() @@ -2617,7 +2075,7 @@ static void _opt_list() xfree(str); if (opt.conn_type != (uint16_t) NO_VAL) info("conn_type : %u", opt.conn_type); - str = print_geometry(); + str = print_geometry(opt.geometry); info("geometry : %s", str); xfree(str); info("reboot : %s", opt.reboot ? "no" : "yes"); @@ -2642,17 +2100,23 @@ static void _opt_list() } info("prolog : %s", opt.prolog); info("epilog : %s", opt.epilog); - info("mail_type : %s", _print_mail_type(opt.mail_type)); + info("mail_type : %s", print_mail_type(opt.mail_type)); info("mail_user : %s", opt.mail_user); info("task_prolog : %s", opt.task_prolog); info("task_epilog : %s", opt.task_epilog); info("ctrl_comm_ifhn : %s", opt.ctrl_comm_ifhn); info("multi_prog : %s", opt.multi_prog ? "yes" : "no"); + info("sockets-per-node : %d - %d", opt.min_sockets_per_node, + opt.max_sockets_per_node); + info("cores-per-socket : %d - %d", opt.min_cores_per_socket, + opt.max_cores_per_socket); + info("threads-per-core : %d - %d", opt.min_threads_per_core, + opt.max_threads_per_core); info("ntasks-per-node : %d", opt.ntasks_per_node); info("ntasks-per-socket : %d", opt.ntasks_per_socket); info("ntasks-per-core : %d", opt.ntasks_per_core); info("plane_size : %u", opt.plane_size); - str = print_commandline(); + str = print_commandline(opt.argc, opt.argv); info("remote command : `%s'", str); xfree(str); -- GitLab