blob: 6dfd58ddc2a4b3c7f211c779da8e7de5ef1918e3 [file] [log] [blame]
/*****************************************************************************\
* slurm_resource_info.c - Functions to determine number of available resources
* $Id: slurm_resource_info.c,v 1.12 2006/10/04 21:52:24 palermo Exp $
*****************************************************************************
* Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
* Written by Susanne M. Balle, <susanne.balle@hp.com>
* UCRL-CODE-226842.
*
* 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.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with SLURM; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#if HAVE_STRING_H
# include <string.h>
#endif
#include <sys/types.h>
#include "src/common/log.h"
#include <slurm/slurm.h>
#include "src/common/slurm_resource_info.h"
#if(0)
#define DEBUG 1
#endif
/*
* slurm_get_avail_procs - Get the number of "available" cpus on a node
* given this number given the number of cpus_per_task and
* maximum sockets, cores, threads. Note that the value of
* cpus is the lowest-level logical processor (LLLP).
* IN mxsockets - Job requested max sockets
* IN mxcores - Job requested max cores
* IN mxthreads - Job requested max threads
* IN minsockets - Job requested min sockets
* IN mincores - Job requested min cores
* IN cpuspertask - Job requested cpus per task
* IN ntaskspernode - number of tasks per node
* IN ntaskspersocket- number of tasks per socket
* IN ntaskspercore - number of tasks per core
* IN/OUT cpus - Available cpu count
* IN/OUT sockets - Available socket count
* IN/OUT cores - Available core count
* IN/OUT threads - Available thread count
* IN alloc_sockets - Allocated socket count to other jobs
* IN alloc_lps - Allocated cpu count to other jobs
* IN cr_type - Consumable Resource type
*
* Note: used in both the select/{linear,cons_res} plugins.
*/
int slurm_get_avail_procs(const uint16_t mxsockets,
const uint16_t mxcores,
const uint16_t mxthreads,
const uint16_t minsockets,
const uint16_t mincores,
const uint16_t cpuspertask,
const uint16_t ntaskspernode,
const uint16_t ntaskspersocket,
const uint16_t ntaskspercore,
uint16_t *cpus,
uint16_t *sockets,
uint16_t *cores,
uint16_t *threads,
const uint16_t alloc_sockets,
const uint16_t *alloc_cores,
const uint16_t alloc_lps,
const select_type_plugin_info_t cr_type,
uint32_t job_id,
char *name)
{
uint16_t avail_cpus = 0, max_cpus = 0;
uint16_t max_avail_cpus = 0xffff; /* for alloc_* accounting */
uint16_t max_sockets = mxsockets;
uint16_t max_cores = mxcores;
uint16_t max_threads = mxthreads;
uint16_t min_sockets = minsockets;
uint16_t min_cores = mincores;
uint16_t cpus_per_task = cpuspertask;
int i;
/* pick defaults for any unspecified items */
if (cpus_per_task <= 0)
cpus_per_task = 1;
if (*threads <= 0)
*threads = 1;
if (*cores <= 0)
*cores = 1;
if (*sockets <= 0)
*sockets = *cpus / *cores / *threads;
#if(DEBUG)
info("get_avail_procs %u %s MAX User_ sockets %u cores %u threads %u",
job_id, name, max_sockets, max_cores, max_threads);
info("get_avail_procs %u %s MIN User_ sockets %u cores %u",
job_id, name, min_sockets, min_cores);
info("get_avail_procs %u %s HW_ sockets %u cores %u threads %u",
job_id, name, *sockets, *cores, *threads);
info("get_avail_procs %u %s Ntask node %u sockets %u core %u",
job_id, name, ntaskspernode, ntaskspersocket,
ntaskspercore);
info("get_avail_procs %u %s cr_type %d cpus %u Allocated sockets %u lps %u",
job_id, name, cr_type, *cpus, alloc_sockets, alloc_lps);
if (((cr_type == CR_CORE) || (cr_type == CR_CORE_MEMORY))
&& (alloc_lps != 0)) {
for (i = 0; i < *sockets; i++)
info("get_avail_procs %u %s alloc_cores[%d] = %u",
job_id, name, i, alloc_cores[i]);
}
#endif
switch(cr_type) {
/* For the following CR types, nodes have no notion of socket, core,
and thread. Only one level of logical processors */
case CR_CPU:
case CR_CPU_MEMORY:
case CR_MEMORY:
switch(cr_type) {
case CR_CPU:
case CR_CPU_MEMORY:
if (*cpus >= alloc_lps)
*cpus -= alloc_lps;
else {
*cpus = 0;
error("cons_res: *cpus underflow");
}
break;
default:
break;
}
/*** compute an overall maximum cpu count honoring ntasks* ***/
max_cpus = *cpus;
if (ntaskspernode > 0) {
max_cpus = MIN(max_cpus, ntaskspernode);
}
break;
/* For all other types, nodes contain sockets, cores, and threads */
case CR_CORE:
case CR_CORE_MEMORY:
if (*cpus >= alloc_lps)
*cpus -= alloc_lps;
else {
*cpus = 0;
error("cons_res: *cpus underflow");
}
if (alloc_lps > 0) {
max_avail_cpus = 0;
int tmp_diff = 0;
for (i=0; i<*sockets; i++) {
tmp_diff = *cores - alloc_cores[i];
if (min_cores <= tmp_diff)
max_avail_cpus += tmp_diff;
}
}
/*** honor socket/core/thread maximums ***/
*sockets = MIN(*sockets, max_sockets);
*threads = MIN(*threads, max_threads);
*cores = MIN(*cores, max_cores);
if (min_sockets > *sockets) {
*cpus = 0;
} else {
int max_cpus_socket = 0;
max_cpus = 0;
for (i=0; i<*sockets; i++) {
max_cpus_socket = 0;
if (min_cores <= *cores) {
int num_threads = *threads;
if (ntaskspercore > 0) {
num_threads = MIN(num_threads,
ntaskspercore);
}
max_cpus_socket = *cores * num_threads;
}
if (ntaskspersocket > 0) {
max_cpus_socket = MIN(max_cpus_socket,
ntaskspersocket);
}
max_cpus += max_cpus_socket;
}
max_cpus = MIN(max_cpus, max_avail_cpus);
}
/*** honor any availability maximum ***/
max_cpus = MIN(max_cpus, max_avail_cpus);
if (ntaskspernode > 0) {
max_cpus = MIN(max_cpus, ntaskspernode);
}
break;
case CR_SOCKET:
case CR_SOCKET_MEMORY:
default:
if (*sockets >= alloc_sockets)
*sockets -= alloc_sockets; /* sockets count */
else {
*sockets = 0;
error("cons_res: *sockets underflow");
}
if (*cpus >= alloc_lps)
*cpus -= alloc_lps;
else {
*cpus = 0;
error("cons_res: *cpus underflow");
}
/*** honor socket/core/thread maximums ***/
*sockets = MIN(*sockets, max_sockets);
*cores = MIN(*cores, max_cores);
*threads = MIN(*threads, max_threads);
if (min_sockets > *sockets)
*cpus = 0;
/*** compute an overall maximum cpu count honoring ntasks* ***/
max_cpus = *threads;
if (ntaskspercore > 0) {
max_cpus = MIN(max_cpus, ntaskspercore);
}
max_cpus *= *cores;
if (ntaskspersocket > 0) {
max_cpus = MIN(max_cpus, ntaskspersocket);
}
max_cpus *= *sockets;
if (ntaskspernode > 0) {
max_cpus = MIN(max_cpus, ntaskspernode);
}
/*** honor any availability maximum ***/
max_cpus = MIN(max_cpus, max_avail_cpus);
break;
}
/*** factor cpus_per_task into max_cpus ***/
max_cpus *= cpus_per_task;
/*** round down available based on cpus_per_task ***/
avail_cpus = (*cpus / cpus_per_task) * cpus_per_task;
avail_cpus = MIN(avail_cpus, max_cpus);
#if(DEBUG)
info("get_avail_procs %u %s return cpus %u sockets %u cores %u threads %u",
job_id, name, *cpus, *sockets, *cores, *threads);
info("get_avail_procs %d %s avail_cpus %u", job_id, name, avail_cpus);
#endif
return(avail_cpus);
}
/*
* slurm_sprint_cpu_bind_type
*
* Given a cpu_bind_type, report all flag settings in str
* IN - cpu_bind_type
* OUT - str
*/
void slurm_sprint_cpu_bind_type(char *str, cpu_bind_type_t cpu_bind_type)
{
if (!str)
return;
str[0] = '\0';
if (cpu_bind_type & CPU_BIND_TO_THREADS)
strcat(str, "threads,");
if (cpu_bind_type & CPU_BIND_TO_CORES)
strcat(str, "cores,");
if (cpu_bind_type & CPU_BIND_TO_SOCKETS)
strcat(str, "sockets,");
if (cpu_bind_type & CPU_BIND_VERBOSE)
strcat(str, "verbose,");
if (cpu_bind_type & CPU_BIND_NONE)
strcat(str, "none,");
if (cpu_bind_type & CPU_BIND_RANK)
strcat(str, "rank,");
if (cpu_bind_type & CPU_BIND_MAP)
strcat(str, "mapcpu,");
if (cpu_bind_type & CPU_BIND_MASK)
strcat(str, "maskcpu,");
if (*str) {
str[strlen(str)-1] = '\0'; /* remove trailing ',' */
} else {
strcat(str, "(null type)"); /* no bits set */
}
}
/*
* slurm_sprint_mem_bind_type
*
* Given a mem_bind_type, report all flag settings in str
* IN - mem_bind_type
* OUT - str
*/
void slurm_sprint_mem_bind_type(char *str, mem_bind_type_t mem_bind_type)
{
if (!str)
return;
str[0] = '\0';
if (mem_bind_type & MEM_BIND_VERBOSE)
strcat(str, "verbose,");
if (mem_bind_type & MEM_BIND_NONE)
strcat(str, "none,");
if (mem_bind_type & MEM_BIND_RANK)
strcat(str, "rank,");
if (mem_bind_type & MEM_BIND_LOCAL)
strcat(str, "local,");
if (mem_bind_type & MEM_BIND_MAP)
strcat(str, "mapmem,");
if (mem_bind_type & MEM_BIND_MASK)
strcat(str, "maskmem,");
if (*str) {
str[strlen(str)-1] = '\0'; /* remove trailing ',' */
} else {
strcat(str, "(null type)"); /* no bits set */
}
}