| /*****************************************************************************\ |
| * 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 */ |
| } |
| } |