| /*****************************************************************************\ | |
| * node_conf.c - partially manage the node records of slurm | |
| * (see src/slurmctld/node_mgr.c for the set of functionalities | |
| * related to slurmctld usage of nodes) | |
| * Note: there is a global node table (node_record_table_ptr), its | |
| * hash table (node_hash_table), time stamp (last_node_update) and | |
| * configuration list (config_list) | |
| ***************************************************************************** | |
| * Copyright (C) 2002-2007 The Regents of the University of California. | |
| * Copyright (C) 2008-2010 Lawrence Livermore National Security. | |
| * Copyright (C) SchedMD LLC. | |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | |
| * Written by Morris Jette <jette1@llnl.gov> et. al. | |
| * CODE-OCEC-09-009. All rights reserved. | |
| * | |
| * This file is part of Slurm, a resource management program. | |
| * For details, see <https://slurm.schedmd.com/>. | |
| * Please also read the included file: DISCLAIMER. | |
| * | |
| * Slurm is free software; you can redistribute it and/or modify it under | |
| * the terms of the GNU General Public License as published by the Free | |
| * Software Foundation; either version 2 of the License, or (at your option) | |
| * any later version. | |
| * | |
| * In addition, as a special exception, the copyright holders give permission | |
| * to link the code of portions of this program with the OpenSSL library under | |
| * certain conditions as described in each individual source file, and | |
| * distribute linked combinations including the two. You must obey the GNU | |
| * General Public License in all respects for all of the code used other than | |
| * OpenSSL. If you modify file(s) with this exception, you may extend this | |
| * exception to your version of the file(s), but you are not obligated to do | |
| * so. If you do not wish to do so, delete this exception statement from your | |
| * version. If you delete this exception statement from all source files in | |
| * the program, then also delete it here. | |
| * | |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY | |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
| * details. | |
| * | |
| * You should have received a copy of the GNU General Public License along | |
| * with Slurm; if not, write to the Free Software Foundation, Inc., | |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| \*****************************************************************************/ | |
| #include "config.h" | |
| #include <ctype.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <inttypes.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <time.h> | |
| #include "src/common/assoc_mgr.h" | |
| #include "src/common/drained_tres.h" | |
| #include "src/common/hostlist.h" | |
| #include "src/common/macros.h" | |
| #include "src/common/node_features.h" | |
| #include "src/common/pack.h" | |
| #include "src/common/pack_data.h" | |
| #include "src/common/parse_time.h" | |
| #include "src/common/read_config.h" | |
| #include "src/common/slurm_protocol_pack.h" | |
| #include "src/common/xassert.h" | |
| #include "src/common/xmalloc.h" | |
| #include "src/common/xstring.h" | |
| #include "src/interfaces/accounting_storage.h" | |
| #include "src/interfaces/acct_gather_energy.h" | |
| #include "src/interfaces/gres.h" | |
| #include "src/interfaces/select.h" | |
| #include "src/interfaces/topology.h" | |
| #define _DEBUG 0 | |
| typedef struct { | |
| list_t *recs; | |
| uint16_t tpc; | |
| } drained_cpu_fmt_t; | |
| strong_alias(init_node_conf, slurm_init_node_conf); | |
| strong_alias(build_all_nodeline_info, slurm_build_all_nodeline_info); | |
| strong_alias(rehash_node, slurm_rehash_node); | |
| strong_alias(hostlist2bitmap, slurm_hostlist2bitmap); | |
| strong_alias(bitmap2node_name, slurm_bitmap2node_name); | |
| strong_alias(node_name2bitmap, slurm_node_name2bitmap); | |
| strong_alias(find_node_record, slurm_find_node_record); | |
| /* Global variables */ | |
| list_t *config_list = NULL; /* list of config_record entries */ | |
| time_t last_node_update = (time_t) 0; /* time of last update */ | |
| node_record_t **node_record_table_ptr = NULL; /* node records */ | |
| node_select_stats_t **node_select_stats_array = NULL; /* select plugin stats */ | |
| xhash_t* node_hash_table = NULL; | |
| int node_record_table_size = 0; /* size of node_record_table_ptr */ | |
| int node_record_count = 0; /* number of node slots in | |
| * node_record_table_ptr */ | |
| int active_node_record_count = 0; /* non-null node count in | |
| * node_record_table_ptr */ | |
| int last_node_index = -1; /* index of last node in table */ | |
| uint16_t *cr_node_num_cores = NULL; | |
| uint32_t *cr_node_cores_offset = NULL; | |
| bool spec_cores_first = false; | |
| time_t slurmd_start_time = 0; | |
| /* | |
| * Old-to-new node index mapping built during load_all_node_state() | |
| * so that GRES per-node arrays can be remapped after the node table | |
| * is re-sorted. Populated in slurmctld's node_mgr.c. | |
| */ | |
| int *node_old_to_new_map = NULL; | |
| uint32_t old_node_record_count = 0; | |
| bool is_node_table_changed = false; | |
| /* Local function definitions */ | |
| static void _delete_config_record(void); | |
| static void _delete_node_config_ptr(node_record_t *node_ptr); | |
| #if _DEBUG | |
| static void _dump_hash(void); | |
| #endif | |
| static node_record_t *_find_node_record(char *name, bool test_alias, | |
| bool log_missing); | |
| static void _list_delete_config(void *config_entry); | |
| static void _node_record_hash_identity(void *item, const void **key, | |
| uint32_t *key_len); | |
| /* | |
| * _delete_config_record - delete all configuration records | |
| * RET 0 if no error, errno otherwise | |
| * global: config_list - list of all configuration records | |
| */ | |
| static void _delete_config_record(void) | |
| { | |
| last_node_update = time (NULL); | |
| list_flush(config_list); | |
| } | |
| #if _DEBUG | |
| /* | |
| * helper function used by _dump_hash to print the hash table elements | |
| */ | |
| static void xhash_walk_helper_cbk(void *item, void *arg) | |
| { | |
| int *i_ptr = arg; | |
| node_record_t *node_ptr = (node_record_t *) item; | |
| debug3("node_hash[%d]:%d(%s)", (*i_ptr)++, node_ptr->index, | |
| node_ptr->name); | |
| } | |
| /* | |
| * _dump_hash - print the node_hash_table contents, used for debugging | |
| * or analysis of hash technique | |
| * global: node_record_table_ptr - pointer to global node table | |
| * node_hash_table - table of hash indexes | |
| */ | |
| static void _dump_hash(void) | |
| { | |
| int i = 0; | |
| if (node_hash_table == NULL) | |
| return; | |
| debug2("node_hash: indexing %u elements", | |
| xhash_count(node_hash_table)); | |
| xhash_walk(node_hash_table, xhash_walk_helper_cbk, &i); | |
| } | |
| #endif | |
| /* _list_delete_config - delete an entry from the config list, | |
| * see list.h for documentation */ | |
| static void _list_delete_config(void *config_entry) | |
| { | |
| config_record_t *config_ptr = (config_record_t *) config_entry; | |
| xassert(config_ptr); | |
| xassert(config_ptr->magic == CONFIG_MAGIC); | |
| xfree(config_ptr->cpu_spec_list); | |
| xfree(config_ptr->feature); | |
| xfree(config_ptr->gres); | |
| xfree(config_ptr->max_drained_tres); | |
| xfree (config_ptr->nodes); | |
| FREE_NULL_BITMAP (config_ptr->node_bitmap); | |
| xfree(config_ptr->topology_str); | |
| xfree(config_ptr->tres_weights); | |
| xfree(config_ptr->tres_weights_str); | |
| xfree (config_ptr); | |
| } | |
| /* | |
| * xhash helper function to index node_record per name field | |
| * in node_hash_table | |
| */ | |
| static void _node_record_hash_identity(void *item, const void **key, | |
| uint32_t *key_len) | |
| { | |
| node_record_t *node_ptr = item; | |
| *key = node_ptr->name; | |
| *key_len = strlen(node_ptr->name); | |
| } | |
| /* | |
| * bitmap2hostlist - given a bitmap, build a hostlist | |
| * IN bitmap - bitmap pointer | |
| * RET pointer to hostlist or NULL on error | |
| * globals: node_record_table_ptr - pointer to node table | |
| * NOTE: the caller must xfree the memory at node_list when no longer required | |
| */ | |
| extern hostlist_t *bitmap2hostlist(bitstr_t *bitmap) | |
| { | |
| hostlist_t *hl; | |
| node_record_t *node_ptr; | |
| if (bitmap == NULL) | |
| return NULL; | |
| hl = hostlist_create(NULL); | |
| for (int i = 0; (node_ptr = next_node_bitmap(bitmap, &i)); i++) { | |
| hostlist_push_host(hl, node_ptr->name); | |
| } | |
| return hl; | |
| } | |
| /* | |
| * bitmap2node_name_sortable - given a bitmap, build a list of comma | |
| * separated node names. names may include regular expressions | |
| * (e.g. "lx[01-10]") | |
| * IN bitmap - bitmap pointer | |
| * IN sort - returned sorted list or not | |
| * RET pointer to node list or NULL on error | |
| * globals: node_record_table_ptr - pointer to node table | |
| * NOTE: the caller must xfree the memory at node_list when no longer required | |
| */ | |
| extern char *bitmap2node_name_sortable(bitstr_t *bitmap, bool sort) | |
| { | |
| hostlist_t *hl; | |
| char *buf; | |
| hl = bitmap2hostlist (bitmap); | |
| if (hl == NULL) | |
| return xstrdup(""); | |
| if (sort) | |
| hostlist_sort(hl); | |
| buf = hostlist_ranged_string_xmalloc(hl); | |
| hostlist_destroy(hl); | |
| return buf; | |
| } | |
| /* | |
| * bitmap2node_name - given a bitmap, build a list of sorted, comma | |
| * separated node names. names may include regular expressions | |
| * (e.g. "lx[01-10]") | |
| * IN bitmap - bitmap pointer | |
| * RET pointer to node list or NULL on error | |
| * globals: node_record_table_ptr - pointer to node table | |
| * NOTE: the caller must xfree the memory at node_list when no longer required | |
| */ | |
| extern char *bitmap2node_name(bitstr_t *bitmap) | |
| { | |
| return bitmap2node_name_sortable(bitmap, 1); | |
| } | |
| extern int node_rank_order_cmp(const void *x, const void *y) | |
| { | |
| const node_rank_order_t *nx = x; | |
| const node_rank_order_t *ny = y; | |
| if (nx->node_rank > ny->node_rank) | |
| return 1; | |
| else if (nx->node_rank < ny->node_rank) | |
| return -1; | |
| if (nx->node_inx > ny->node_inx) | |
| return 1; | |
| else if (nx->node_inx < ny->node_inx) | |
| return -1; | |
| return 0; | |
| } | |
| static int _check_callback(char *alias, char *hostname, | |
| char *address, char *bcast_address, | |
| uint16_t port, int state_val, | |
| slurm_conf_node_t *node_ptr, | |
| config_record_t *config_ptr) | |
| { | |
| int rc = SLURM_SUCCESS; | |
| node_record_t *node_rec; | |
| if ((node_rec = find_node_record2(alias))) | |
| fatal("Duplicated NodeHostName %s in config file", alias); | |
| if ((rc = create_node_record(config_ptr, alias, &node_rec))) | |
| return rc; | |
| if ((state_val != NO_VAL) && | |
| (state_val != NODE_STATE_UNKNOWN)) | |
| node_rec->node_state = state_val; | |
| node_rec->last_response = (time_t) 0; | |
| node_rec->comm_name = xstrdup(address); | |
| node_rec->cpu_bind = node_ptr->cpu_bind; | |
| node_rec->node_hostname = xstrdup(hostname); | |
| node_rec->bcast_address = xstrdup(bcast_address); | |
| node_rec->parameters = xstrdup(node_ptr->parameters); | |
| node_rec->port = port; | |
| node_rec->features = xstrdup(node_ptr->feature); | |
| node_rec->reason = xstrdup(node_ptr->reason); | |
| return rc; | |
| } | |
| extern config_record_t *config_record_from_conf_node( | |
| slurm_conf_node_t *conf_node, int tres_cnt) | |
| { | |
| config_record_t *config_ptr; | |
| config_ptr = create_config_record(); | |
| config_ptr->boards = conf_node->boards; | |
| config_ptr->core_spec_cnt = conf_node->core_spec_cnt; | |
| config_ptr->cores = conf_node->cores; | |
| config_ptr->cpu_bind = conf_node->cpu_bind; | |
| config_ptr->cpu_spec_list = xstrdup(conf_node->cpu_spec_list); | |
| config_ptr->cpus = conf_node->cpus; | |
| if (conf_node->feature && conf_node->feature[0]) | |
| config_ptr->feature = xstrdup(conf_node->feature); | |
| if (conf_node->max_drained_tres && conf_node->max_drained_tres[0]) { | |
| drained_tres_validate_max(conf_node->max_drained_tres, | |
| conf_node->nodenames); | |
| config_ptr->max_drained_tres = | |
| xstrdup(conf_node->max_drained_tres); | |
| } | |
| config_ptr->mem_spec_limit = conf_node->mem_spec_limit; | |
| config_ptr->nodes = xstrdup(conf_node->nodenames); | |
| config_ptr->real_memory = conf_node->real_memory; | |
| config_ptr->res_cores_per_gpu = conf_node->res_cores_per_gpu; | |
| config_ptr->suspend_time = conf_node->suspend_time; | |
| config_ptr->threads = conf_node->threads; | |
| config_ptr->tmp_disk = conf_node->tmp_disk; | |
| config_ptr->topology_str = xstrdup(conf_node->topology_str); | |
| config_ptr->tot_sockets = conf_node->tot_sockets; | |
| config_ptr->weight = conf_node->weight; | |
| if (tres_cnt) { | |
| config_ptr->tres_weights_str = | |
| xstrdup(conf_node->tres_weights_str); | |
| config_ptr->tres_weights = | |
| slurm_get_tres_weight_array(conf_node->tres_weights_str, | |
| tres_cnt, true); | |
| } | |
| if (run_in_daemon(IS_SLURMCTLD | IS_SLURMD)) { | |
| config_ptr->gres = gres_name_filter(conf_node->gres, | |
| conf_node->nodenames); | |
| } | |
| /* | |
| * NOTE: Although hres_str is in slurm_conf_node_t, hres_str/hres_info | |
| * is intentionally not included in config_ptr. The baseline HRES layer | |
| * membership is kept per layer in the cluster license list and, for | |
| * dynamic nodes, in node_record_t.hres_list_config. Excluding it from | |
| * config_ptr keeps nodes with different HRES layers in the same config | |
| * record. | |
| */ | |
| return config_ptr; | |
| } | |
| /* | |
| * build_all_nodeline_info - get a array of slurm_conf_node_t structures | |
| * from the slurm.conf reader, build table, and set values | |
| * IN set_bitmap - if true then set node_bitmap in config record (used by | |
| * slurmd), false is used by slurmctld, clients, and testsuite | |
| * IN tres_cnt - number of TRES configured on system (used on controller side) | |
| */ | |
| extern int build_all_nodeline_info(bool set_bitmap, int tres_cnt) | |
| { | |
| slurm_conf_node_t *node, **ptr_array; | |
| config_record_t *config_ptr = NULL; | |
| int count, i; | |
| count = slurm_conf_nodename_array(&ptr_array); | |
| for (i = 0; i < count; i++) { | |
| int rc; | |
| node = ptr_array[i]; | |
| config_ptr = config_record_from_conf_node(node, tres_cnt); | |
| if (((rc = expand_nodeline_info(node, config_ptr, NULL, | |
| _check_callback)))) | |
| return rc; | |
| } | |
| if (set_bitmap) { | |
| list_itr_t *config_iterator; | |
| config_iterator = list_iterator_create(config_list); | |
| while ((config_ptr = list_next(config_iterator))) { | |
| node_name2bitmap(config_ptr->nodes, true, | |
| &config_ptr->node_bitmap, NULL); | |
| } | |
| list_iterator_destroy(config_iterator); | |
| } | |
| return SLURM_SUCCESS; | |
| } | |
| extern int build_node_spec_bitmap(node_record_t *node_ptr) | |
| { | |
| uint32_t size; | |
| int *cpu_spec_array; | |
| int i; | |
| if (node_ptr->tpc == 0) { | |
| error("Node %s has invalid thread per core count (%u)", | |
| node_ptr->name, node_ptr->tpc); | |
| return SLURM_ERROR; | |
| } | |
| if (!node_ptr->cpu_spec_list) | |
| return SLURM_SUCCESS; | |
| size = node_ptr->tot_cores; | |
| FREE_NULL_BITMAP(node_ptr->node_spec_bitmap); | |
| node_ptr->node_spec_bitmap = bit_alloc(size); | |
| bit_set_all(node_ptr->node_spec_bitmap); | |
| /* remove node's specialized cpus now */ | |
| cpu_spec_array = bitfmt2int(node_ptr->cpu_spec_list); | |
| i = 0; | |
| while (cpu_spec_array[i] != -1) { | |
| int start = (cpu_spec_array[i] / node_ptr->tpc); | |
| int end = (cpu_spec_array[i + 1] / node_ptr->tpc); | |
| if (start > size) { | |
| error("%s: Specialized CPUs id start above the configured limit.", | |
| __func__); | |
| break; | |
| } | |
| if (end > size) { | |
| error("%s: Specialized CPUs id end above the configured limit", | |
| __func__); | |
| end = size; | |
| } | |
| /* | |
| * We need to test to make sure we have these bits in this map. | |
| * If the node goes from 12 cpus to 6 like scenario. | |
| */ | |
| bit_nclear(node_ptr->node_spec_bitmap, start, end); | |
| i += 2; | |
| } | |
| node_ptr->core_spec_cnt = bit_clear_count(node_ptr->node_spec_bitmap); | |
| xfree(cpu_spec_array); | |
| return SLURM_SUCCESS; | |
| } | |
| /* | |
| * Select cores and CPUs to be reserved for core specialization. | |
| */ | |
| extern void node_conf_select_spec_cores(node_record_t *node_ptr) | |
| { | |
| int spec_cores, res_core, res_sock, res_off; | |
| int from_core, to_core, incr_core, from_sock, to_sock, incr_sock; | |
| bitstr_t *cpu_spec_bitmap; | |
| spec_cores = node_ptr->core_spec_cnt; | |
| cpu_spec_bitmap = bit_alloc(node_ptr->cpus); | |
| node_ptr->node_spec_bitmap = bit_alloc(node_ptr->tot_cores); | |
| bit_set_all(node_ptr->node_spec_bitmap); | |
| if (spec_cores_first) { | |
| from_core = 0; | |
| to_core = node_ptr->cores; | |
| incr_core = 1; | |
| from_sock = 0; | |
| to_sock = node_ptr->tot_sockets; | |
| incr_sock = 1; | |
| } else { | |
| from_core = node_ptr->cores - 1; | |
| to_core = -1; | |
| incr_core = -1; | |
| from_sock = node_ptr->tot_sockets - 1; | |
| to_sock = -1; | |
| incr_sock = -1; | |
| } | |
| for (res_core = from_core; | |
| (spec_cores && (res_core != to_core)); res_core += incr_core) { | |
| for (res_sock = from_sock; | |
| (spec_cores && (res_sock != to_sock)); | |
| res_sock += incr_sock) { | |
| int thread_off; | |
| thread_off = ((res_sock * node_ptr->cores) + res_core) * | |
| node_ptr->tpc; | |
| bit_nset(cpu_spec_bitmap, thread_off, | |
| thread_off + node_ptr->tpc - 1); | |
| res_off = (res_sock * node_ptr->cores) + res_core; | |
| bit_clear(node_ptr->node_spec_bitmap, res_off); | |
| spec_cores--; | |
| } | |
| } | |
| node_ptr->cpu_spec_list = bit_fmt_full(cpu_spec_bitmap); | |
| FREE_NULL_BITMAP(cpu_spec_bitmap); | |
| return; | |
| } | |
| /* | |
| * Expand a nodeline's node names, host names, addrs, ports into separate nodes. | |
| */ | |
| extern int expand_nodeline_info(slurm_conf_node_t *node_ptr, | |
| config_record_t *config_ptr, | |
| char **err_msg, | |
| int (*_callback) ( | |
| char *alias, char *hostname, | |
| char *address, char *bcast_address, | |
| uint16_t port, int state_val, | |
| slurm_conf_node_t *node_ptr, | |
| config_record_t *config_ptr)) | |
| { | |
| hostlist_t *address_list = NULL; | |
| hostlist_t *alias_list = NULL; | |
| hostlist_t *bcast_list = NULL; | |
| hostlist_t *hostname_list = NULL; | |
| hostlist_t *port_list = NULL; | |
| char *address = NULL; | |
| char *alias = NULL; | |
| char *bcast_address = NULL; | |
| char *hostname = NULL; | |
| char *port_str = NULL; | |
| int state_val = NODE_STATE_UNKNOWN, rc = SLURM_SUCCESS; | |
| int address_count, alias_count, bcast_count, hostname_count, port_count; | |
| uint16_t port = slurm_conf.slurmd_port; | |
| if (!node_ptr->nodenames || !node_ptr->nodenames[0]) | |
| fatal("Empty NodeName in config."); | |
| if (node_ptr->state) { | |
| state_val = state_str2int(node_ptr->state, node_ptr->nodenames); | |
| if (state_val == NO_VAL) | |
| fatal("Invalid state %s from %s", | |
| node_ptr->state, node_ptr->nodenames); | |
| } | |
| if (!(address_list = hostlist_create(node_ptr->addresses))) | |
| fatal("Unable to create NodeAddr list from %s", | |
| node_ptr->addresses); | |
| if (!(alias_list = hostlist_create(node_ptr->nodenames))) | |
| fatal("Unable to create NodeName list from %s", | |
| node_ptr->nodenames); | |
| if (!(bcast_list = hostlist_create(node_ptr->bcast_addresses))) | |
| fatal("Unable to create BcastAddr list from %s", | |
| node_ptr->bcast_addresses); | |
| if (!(hostname_list = hostlist_create(node_ptr->hostnames))) | |
| fatal("Unable to create NodeHostname list from %s", | |
| node_ptr->hostnames); | |
| if (node_ptr->port_str && node_ptr->port_str[0] && | |
| (node_ptr->port_str[0] != '[') && | |
| (strchr(node_ptr->port_str, '-') || | |
| strchr(node_ptr->port_str, ','))) { | |
| xstrfmtcat(port_str, "[%s]", node_ptr->port_str); | |
| port_list = hostlist_create(port_str); | |
| xfree(port_str); | |
| } else | |
| port_list = hostlist_create(node_ptr->port_str); | |
| if (!port_list) | |
| fatal("Unable to create Port list from %s", | |
| node_ptr->port_str); | |
| /* some sanity checks */ | |
| address_count = hostlist_count(address_list); | |
| bcast_count = hostlist_count(bcast_list); | |
| alias_count = hostlist_count(alias_list); | |
| hostname_count = hostlist_count(hostname_list); | |
| port_count = hostlist_count(port_list); | |
| if ((address_count != alias_count) && (address_count != 1)) | |
| fatal("NodeAddr count must equal that of NodeName records or there must be no more than one"); | |
| if ((bcast_count != alias_count) && (bcast_count > 1)) | |
| fatal("BcastAddr count must equal that of NodeName records or there must be no more than one"); | |
| if ((hostname_count != alias_count) && (hostname_count != 1)) | |
| fatal("NodeHostname count must equal that of NodeName records or there must be no more than one"); | |
| if ((port_count != alias_count) && (port_count > 1)) | |
| fatal("Port count must equal that of NodeName records or there must be no more than one (%u != %u)", | |
| port_count, alias_count); | |
| /* now build the individual node structures */ | |
| while ((alias = hostlist_shift(alias_list))) { | |
| if (address_count > 0) { | |
| address_count--; | |
| xfree(address); | |
| address = hostlist_shift(address_list); | |
| } | |
| if (bcast_count > 0) { | |
| bcast_count--; | |
| xfree(bcast_address); | |
| bcast_address = hostlist_shift(bcast_list); | |
| } | |
| if (hostname_count > 0) { | |
| hostname_count--; | |
| xfree(hostname); | |
| hostname = hostlist_shift(hostname_list); | |
| } | |
| if (port_count > 0) { | |
| int port_int; | |
| port_count--; | |
| xfree(port_str); | |
| port_str = hostlist_shift(port_list); | |
| port_int = atoi(port_str); | |
| if ((port_int <= 0) || (port_int > 0xffff)) | |
| fatal("Invalid Port %s", node_ptr->port_str); | |
| port = port_int; | |
| } | |
| if ((rc = (*_callback)(alias, hostname, address, bcast_address, | |
| port, state_val, node_ptr, | |
| config_ptr))) { | |
| if (err_msg) { | |
| xfree(*err_msg); | |
| *err_msg = xstrdup_printf("%s (%s)", | |
| slurm_strerror(rc), | |
| alias); | |
| } | |
| xfree(alias); | |
| break; | |
| } | |
| xfree(alias); | |
| } | |
| /* free allocated storage */ | |
| xfree(address); | |
| xfree(bcast_address); | |
| xfree(hostname); | |
| xfree(port_str); | |
| FREE_NULL_HOSTLIST(address_list); | |
| FREE_NULL_HOSTLIST(alias_list); | |
| FREE_NULL_HOSTLIST(bcast_list); | |
| FREE_NULL_HOSTLIST(hostname_list); | |
| FREE_NULL_HOSTLIST(port_list); | |
| return rc; | |
| } | |
| /* | |
| * Sync with _init_conf_node(). | |
| * | |
| * _init_conf_node() initializes default values from slurm.conf parameters. | |
| * After parsing slurm.conf, build_all_nodeline_info() copies slurm_conf_node_t | |
| * to config_record_t. Defaults values between slurm_conf_node_t and | |
| * config_record_t should stay in sync in case a config_record is created | |
| * outside of slurm.conf parsing. | |
| */ | |
| static void _init_config_record(config_record_t *config_ptr) | |
| { | |
| config_ptr->magic = CONFIG_MAGIC; | |
| config_ptr->boards = 1; | |
| config_ptr->cores = 1; | |
| config_ptr->cpus = 1; | |
| config_ptr->real_memory = 1; | |
| config_ptr->suspend_time = NO_VAL; | |
| config_ptr->threads = 1; | |
| config_ptr->tot_sockets = 1; | |
| config_ptr->weight = 1; | |
| } | |
| /* | |
| * create_config_record - create a config_record entry and set is values to | |
| * the defaults. each config record corresponds to a line in the | |
| * slurm.conf file and typically describes the configuration of a | |
| * large number of nodes | |
| * RET pointer to the config_record | |
| * NOTE: memory allocated will remain in existence until | |
| * _delete_config_record() is called to delete all configuration records | |
| */ | |
| extern config_record_t *create_config_record(void) | |
| { | |
| config_record_t *config_ptr = xmalloc(sizeof(*config_ptr)); | |
| _init_config_record(config_ptr); | |
| list_append(config_list, config_ptr); | |
| last_node_update = time (NULL); | |
| return config_ptr; | |
| } | |
| /* | |
| * Convert CPU list to reserve whole cores | |
| * OUT: | |
| * node_ptr->cpu_spec_list | |
| */ | |
| extern void node_conf_convert_cpu_spec_list(node_record_t *node_ptr) | |
| { | |
| bitstr_t *cpu_spec_bitmap; | |
| /* Nothing to convert if threads per core is 1 */ | |
| if (node_ptr->tpc < 2) | |
| return; | |
| /* create CPU bitmap from input CPU list */ | |
| cpu_spec_bitmap = bit_alloc(node_ptr->cpus); | |
| /* | |
| * Expand CPU bitmap to reserve whole cores | |
| * | |
| * Use uint64_t for i instead of the same type as node_ptr->tpc | |
| * (uint16_t) because multiplying two uint16_t values is promoted to a | |
| * signed int. This implicit promotion can lead to unintended sign | |
| * extension when the result is later converted to int64_t in bit_nset() | |
| * arguments. | |
| */ | |
| for (uint64_t i = 0; i < node_ptr->tot_cores; i++) { | |
| if (!bit_test(node_ptr->node_spec_bitmap, i)) { | |
| /* typecast to avoid coverity error */ | |
| bitoff_t start = (bitoff_t) (i * node_ptr->tpc); | |
| bitoff_t end = (bitoff_t) ((i + 1) * node_ptr->tpc - 1); | |
| bit_nset(cpu_spec_bitmap, start, end); | |
| } | |
| } | |
| xfree(node_ptr->cpu_spec_list); | |
| node_ptr->cpu_spec_list = bit_fmt_full(cpu_spec_bitmap); | |
| FREE_NULL_BITMAP(cpu_spec_bitmap); | |
| } | |
| static void _init_node_record(node_record_t *node_ptr, | |
| config_record_t *config_ptr) | |
| { | |
| /* | |
| * Some of these vars will be overwritten when the node actually | |
| * registers. | |
| */ | |
| node_ptr->magic = NODE_MAGIC; | |
| node_ptr->cpu_load = 0; | |
| node_ptr->energy = acct_gather_energy_alloc(1); | |
| node_ptr->free_mem = NO_VAL64; | |
| node_ptr->next_state = NO_VAL; | |
| node_ptr->owner = NO_VAL; | |
| node_ptr->port = slurm_conf.slurmd_port; | |
| node_ptr->protocol_version = SLURM_MIN_PROTOCOL_VERSION; | |
| node_ptr->resume_timeout = NO_VAL; | |
| node_ptr->suspend_timeout = NO_VAL16; | |
| node_ptr->config_ptr = config_ptr; | |
| node_ptr->boards = config_ptr->boards; | |
| node_ptr->core_spec_cnt = config_ptr->core_spec_cnt; | |
| node_ptr->cores = config_ptr->cores; | |
| node_ptr->cpus = config_ptr->cpus; | |
| node_ptr->mem_spec_limit = config_ptr->mem_spec_limit; | |
| node_ptr->real_memory = config_ptr->real_memory; | |
| node_ptr->res_cores_per_gpu = config_ptr->res_cores_per_gpu; | |
| node_ptr->suspend_time = config_ptr->suspend_time; | |
| node_ptr->suspend_time_configured = config_ptr->suspend_time; | |
| node_ptr->threads = config_ptr->threads; | |
| node_ptr->tmp_disk = config_ptr->tmp_disk; | |
| node_ptr->tot_sockets = config_ptr->tot_sockets; | |
| node_ptr->tot_cores = config_ptr->tot_sockets * config_ptr->cores; | |
| node_ptr->weight = config_ptr->weight; | |
| /* | |
| * Here we determine if this node is scheduling threads or not. | |
| * We will set tpc to be the number of schedulable threads per core. | |
| */ | |
| if (node_ptr->tot_cores >= config_ptr->cpus) | |
| node_ptr->tpc = 1; | |
| else | |
| node_ptr->tpc = config_ptr->threads; | |
| node_ptr->cpu_spec_list = xstrdup(config_ptr->cpu_spec_list); | |
| if (node_ptr->cpu_spec_list) { | |
| build_node_spec_bitmap(node_ptr); | |
| node_conf_convert_cpu_spec_list(node_ptr); | |
| } else if (node_ptr->core_spec_cnt) { | |
| node_conf_select_spec_cores(node_ptr); | |
| } | |
| node_ptr->cpus_efctv = node_ptr->cpus - | |
| (node_ptr->core_spec_cnt * node_ptr->tpc); | |
| node_ptr->topology_str = xstrdup(config_ptr->topology_str); | |
| } | |
| extern void grow_node_record_table_ptr(void) | |
| { | |
| node_record_table_size = node_record_count + 100; | |
| if (slurm_conf.max_node_cnt != NO_VAL) | |
| node_record_table_size = MAX(node_record_count, | |
| slurm_conf.max_node_cnt); | |
| xrealloc(node_record_table_ptr, | |
| node_record_table_size * sizeof(node_record_t *)); | |
| xrealloc(node_select_stats_array, | |
| node_record_table_size * sizeof(node_select_stats_t *)); | |
| /* | |
| * You need to rehash the hash after we realloc or we will have | |
| * only bad memory references in the hash. | |
| */ | |
| rehash_node(); | |
| } | |
| /* | |
| * create_node_record - create a node record and set its values to defaults | |
| * IN config_ptr - pointer to node's configuration information | |
| * IN node_name - name of the node | |
| * OUT node_ptr - node_record_t** with created node on SUCCESS, NULL otherwise. | |
| * RET SUCCESS, or error code | |
| * NOTE: allocates memory at node_record_table_ptr that must be xfreed when | |
| * the global node table is no longer required | |
| */ | |
| extern int create_node_record(config_record_t *config_ptr, char *node_name, | |
| node_record_t **node_ptr) | |
| { | |
| xassert(config_ptr); | |
| xassert(node_name); | |
| xassert(node_ptr); | |
| if (node_record_count >= node_record_table_size) | |
| grow_node_record_table_ptr(); | |
| if (!(*node_ptr = create_node_record_at(node_record_count, | |
| node_name, config_ptr))) | |
| return ESLURM_NODE_TABLE_FULL; | |
| node_record_count++; | |
| return SLURM_SUCCESS; | |
| } | |
| extern node_record_t *create_node_record_at(int index, char *node_name, | |
| config_record_t *config_ptr) | |
| { | |
| node_record_t *node_ptr; | |
| last_node_update = time(NULL); | |
| xassert(index <= node_record_count); | |
| xassert(!node_record_table_ptr[index]); | |
| xassert(!node_select_stats_array[index]); | |
| if ((slurm_conf.max_node_cnt != NO_VAL) && | |
| (index >= slurm_conf.max_node_cnt)) { | |
| error("Attempting to create node record past MaxNodeCount:%d", | |
| slurm_conf.max_node_cnt); | |
| return NULL; | |
| } else if (index > MAX_SLURM_NODES) { | |
| error("Attempting to create nodes past max node limit (%d)", | |
| MAX_SLURM_NODES); | |
| return NULL; | |
| } | |
| if (index > last_node_index) | |
| last_node_index = index; | |
| node_select_stats_array[index] = | |
| xmalloc(sizeof(**node_select_stats_array)); | |
| node_ptr = node_record_table_ptr[index] = xmalloc(sizeof(*node_ptr)); | |
| node_ptr->index = index; | |
| node_ptr->name = xstrdup(node_name); | |
| xhash_add(node_hash_table, node_ptr); | |
| active_node_record_count++; | |
| _init_node_record(node_ptr, config_ptr); | |
| return node_ptr; | |
| } | |
| extern int add_node_record(char *alias, config_record_t *config_ptr, | |
| node_record_t **node_ptr) | |
| { | |
| int rc = SLURM_SUCCESS; | |
| xassert(node_ptr); | |
| if ((*node_ptr = find_node_record2(alias))) { | |
| rc = ESLURM_NODE_ALREADY_EXISTS; | |
| goto end; | |
| } | |
| for (int i = 0; i < node_record_count; i++) { | |
| if (node_record_table_ptr[i]) | |
| continue; | |
| if (!(*node_ptr = | |
| create_node_record_at(i, alias, config_ptr))) { | |
| rc = ESLURM_NODE_TABLE_FULL; | |
| goto end; | |
| } | |
| bit_set(config_ptr->node_bitmap, i); | |
| gres_init_node_config((*node_ptr)->config_ptr->gres, | |
| &(*node_ptr)->gres_list); | |
| break; | |
| } | |
| if (!(*node_ptr)) | |
| rc = ESLURM_NODE_TABLE_FULL; | |
| end: | |
| return rc; | |
| } | |
| static int _find_config_ptr(void *x, void *arg) | |
| { | |
| return (x == arg); | |
| } | |
| extern void insert_node_record_at(node_record_t *node_ptr, int index) | |
| { | |
| xassert(node_ptr); | |
| xassert(node_ptr->config_ptr); | |
| if (node_record_table_ptr[index]) { | |
| error("existing node '%s' already exists at index %d, can't add node '%s'", | |
| node_record_table_ptr[index]->name, index, | |
| node_ptr->name); | |
| return; | |
| } | |
| if (index >= node_record_count) { | |
| error("trying to add node '%s' at index %d past node_record_count %d", | |
| node_ptr->name, index, node_record_count); | |
| return; | |
| } | |
| if (index > last_node_index) | |
| last_node_index = index; | |
| if (!node_ptr->config_ptr) | |
| error("node should have config_ptr from previous tables"); | |
| if (!list_find_first(config_list, _find_config_ptr, | |
| node_ptr->config_ptr)) | |
| list_append(config_list, node_ptr->config_ptr); | |
| node_record_table_ptr[index] = node_ptr; | |
| node_select_stats_array[index] = | |
| xmalloc(sizeof(**node_select_stats_array)); | |
| /* | |
| * _build_bitmaps_pre_select() will reset bitmaps on | |
| * start/reconfig. Set here to be consistent in case this is | |
| * called elsewhere. | |
| */ | |
| bit_clear(node_ptr->config_ptr->node_bitmap, node_ptr->index); | |
| node_ptr->index = index; | |
| bit_set(node_ptr->config_ptr->node_bitmap, node_ptr->index); | |
| xhash_add(node_hash_table, node_ptr); | |
| active_node_record_count++; | |
| /* add node to conf node hash tables */ | |
| slurm_conf_remove_node(node_ptr->name); | |
| slurm_conf_add_node(node_ptr); | |
| } | |
| static void _free_node_select_stats(node_select_stats_t *node_stats) | |
| { | |
| if (node_stats) { | |
| xfree(node_stats->alloc_tres_fmt_str); | |
| xfree(node_stats); | |
| } | |
| } | |
| extern void delete_node_record(node_record_t *node_ptr) | |
| { | |
| xassert(node_ptr); | |
| node_record_table_ptr[node_ptr->index] = NULL; | |
| if (node_ptr->index == last_node_index) { | |
| int i = 0; | |
| for (i = last_node_index - 1; i >=0; i--) { | |
| if (node_record_table_ptr[i]) { | |
| last_node_index = i; | |
| break; | |
| } | |
| } | |
| if (i < 0) | |
| last_node_index = -1; | |
| } | |
| active_node_record_count--; | |
| _free_node_select_stats(node_select_stats_array[node_ptr->index]); | |
| node_select_stats_array[node_ptr->index] = NULL; | |
| _delete_node_config_ptr(node_ptr); | |
| purge_node_rec(node_ptr); | |
| } | |
| /* | |
| * find_node_record - find a record for node with specified name | |
| * IN: name - name of the desired node | |
| * RET: pointer to node record or NULL if not found | |
| * NOTE: Logs an error if the node name is NOT found | |
| */ | |
| extern node_record_t *find_node_record(char *name) | |
| { | |
| return _find_node_record(name, true, true); | |
| } | |
| /* | |
| * find_node_record2 - find a record for node with specified name | |
| * IN: name - name of the desired node | |
| * RET: pointer to node record or NULL if not found | |
| * NOTE: Does not log an error if the node name is NOT found | |
| */ | |
| extern node_record_t *find_node_record2(char *name) | |
| { | |
| return _find_node_record(name, true, false); | |
| } | |
| /* | |
| * find_node_record_no_alias - find a record for node with specified name | |
| * without looking at the node's alias (NodeHostName). | |
| * IN: name - name of the desired node | |
| * RET: pointer to node record or NULL if not found | |
| * NOTE: Logs an error if the node name is NOT found | |
| */ | |
| extern node_record_t *find_node_record_no_alias(char *name) | |
| { | |
| return _find_node_record(name, false, true); | |
| } | |
| /* | |
| * _find_node_record - find a record for node with specified name | |
| * IN: name - name of the desired node | |
| * IN: test_alias - if set, also test NodeHostName value | |
| * IN: log_missing - if set, then print an error message if the node is not found | |
| * RET: pointer to node record or NULL if not found | |
| */ | |
| static node_record_t *_find_node_record(char *name, bool test_alias, | |
| bool log_missing) | |
| { | |
| node_record_t *node_ptr; | |
| if ((name == NULL) || (name[0] == '\0')) { | |
| info("%s: passed NULL node name", __func__); | |
| return NULL; | |
| } | |
| /* nothing added yet */ | |
| if (!node_hash_table) | |
| return NULL; | |
| /* try to find via hash table, if it exists */ | |
| if ((node_ptr = xhash_get_str(node_hash_table, name))) { | |
| xassert(node_ptr->magic == NODE_MAGIC); | |
| return node_ptr; | |
| } | |
| if ((node_record_count == 1) && node_record_table_ptr[0] && | |
| (xstrcmp(node_record_table_ptr[0]->name, "localhost") == 0)) | |
| return (node_record_table_ptr[0]); | |
| if (log_missing) | |
| error("%s: lookup failure for node \"%s\"", | |
| __func__, name); | |
| if (test_alias) { | |
| char *alias = slurm_conf_get_nodename(name); | |
| /* look for the alias node record if the user put this in | |
| * instead of what slurm sees the node name as */ | |
| if (!alias) | |
| return NULL; | |
| node_ptr = xhash_get_str(node_hash_table, alias); | |
| if (log_missing) | |
| error("%s: lookup failure for node \"%s\", alias \"%s\"", | |
| __func__, name, alias); | |
| xfree(alias); | |
| return node_ptr; | |
| } | |
| return NULL; | |
| } | |
| /* | |
| * init_node_conf - initialize the node configuration tables and values. | |
| * this should be called before creating any node or configuration | |
| * entries. | |
| */ | |
| extern void init_node_conf(void) | |
| { | |
| last_node_update = time (NULL); | |
| int i; | |
| node_record_t *node_ptr; | |
| for (i = 0; (node_ptr = next_node(&i)); i++) | |
| delete_node_record(node_ptr); | |
| node_record_count = 0; | |
| node_record_table_size = 0; | |
| last_node_index = -1; | |
| xfree(node_record_table_ptr); | |
| xfree(node_select_stats_array); | |
| xhash_free(node_hash_table); | |
| if (config_list) /* delete defunct configuration entries */ | |
| _delete_config_record(); | |
| else { | |
| config_list = list_create (_list_delete_config); | |
| } | |
| if (xstrcasestr(slurm_conf.sched_params, "spec_cores_first")) | |
| spec_cores_first = true; | |
| else | |
| spec_cores_first = false; | |
| } | |
| /* node_fini2 - free memory associated with node records (except bitmaps) */ | |
| extern void node_fini2(void) | |
| { | |
| int i; | |
| node_record_t *node_ptr; | |
| xhash_free(node_hash_table); | |
| for (i = 0; (node_ptr = next_node(&i)); i++) | |
| delete_node_record(node_ptr); | |
| if (config_list) { | |
| /* | |
| * Must free after purge_node_rec as purge_node_rec will remove | |
| * node config_ptr's. | |
| */ | |
| FREE_NULL_LIST(config_list); | |
| } | |
| xfree(node_record_table_ptr); | |
| xfree(node_select_stats_array); | |
| /* | |
| * Don't clear node_record_count because other plugins are relying on | |
| * node_record_count to free arrays on cleanup -- e.g. | |
| * free_core_array(). | |
| * | |
| * node_record_count = 0; | |
| */ | |
| } | |
| extern int node_name_get_inx(char *node_name) | |
| { | |
| node_record_t *node_ptr = NULL; | |
| if (node_name) | |
| node_ptr = find_node_record(node_name); | |
| if (!node_ptr) | |
| return -1; | |
| return node_ptr->index; | |
| } | |
| extern void add_nodes_with_feature_to_bitmap(bitstr_t *bitmap, char *feature) | |
| { | |
| if (avail_feature_list) { | |
| node_feature_t *node_feat_ptr; | |
| if (!(node_feat_ptr = list_find_first_ro(avail_feature_list, | |
| list_find_feature, | |
| feature))) { | |
| debug2("unable to find nodeset feature '%s'", feature); | |
| return; | |
| } | |
| bit_or(bitmap, node_feat_ptr->node_bitmap); | |
| } else { | |
| node_record_t *node_ptr; | |
| /* | |
| * The global feature bitmaps have not been set up at this | |
| * point, so we'll have to scan through the node_record_table | |
| * directly to locate the appropriate records. | |
| */ | |
| for (int i = 0; (node_ptr = next_node(&i)); i++) { | |
| char *features, *tmp, *tok, *last = NULL; | |
| if (!node_ptr->features) | |
| continue; | |
| features = tmp = xstrdup(node_ptr->features); | |
| while ((tok = strtok_r(tmp, ",", &last))) { | |
| if (!xstrcmp(tok, feature)) { | |
| bit_set(bitmap, node_ptr->index); | |
| break; | |
| } | |
| tmp = NULL; | |
| } | |
| xfree(features); | |
| } | |
| } | |
| } | |
| static int _parse_hostlist_function(bitstr_t *node_bitmap, char *node_str) | |
| { | |
| int rc = SLURM_SUCCESS; | |
| char *start_ptr, *end_ptr; | |
| start_ptr = xstrchr(node_str, '{') + 1; | |
| end_ptr = xstrchr(start_ptr, '}'); | |
| if (!end_ptr) { | |
| error("%s: invalid node specified in hostlist function: \"%s\" (missing closing '}')", | |
| __func__, node_str); | |
| return SLURM_ERROR; | |
| } | |
| *end_ptr = '\0'; | |
| if (!xstrncmp("blockwith{", node_str, 10) || | |
| !xstrncmp("ringwith{", node_str, 9) || | |
| !xstrncmp("switchwith{", node_str, 11) || | |
| !xstrncmp("toruswith{", node_str, 10)) { | |
| node_record_t *node_ptr; | |
| bitstr_t *tmp_bitmap = bit_alloc(node_record_count); | |
| node_ptr = _find_node_record(start_ptr, false, true); | |
| if (node_ptr) { | |
| bit_set(tmp_bitmap, node_ptr->index); | |
| topology_g_whole_topo(tmp_bitmap, 0); | |
| bit_or(node_bitmap, tmp_bitmap); | |
| } else { | |
| rc = SLURM_ERROR; | |
| error("%s: invalid node specified in hostlist function: \"%s\"", | |
| __func__, node_str); | |
| } | |
| FREE_NULL_BITMAP(tmp_bitmap); | |
| } else if (!xstrncmp("block{", node_str, 6) || | |
| !xstrncmp("ring{", node_str, 5) || | |
| !xstrncmp("switch{", node_str, 7) || | |
| !xstrncmp("torus{", node_str, 6)) { | |
| bitstr_t *tmp_bitmap = topology_g_get_bitmap(start_ptr); | |
| if (tmp_bitmap) { | |
| bit_or(node_bitmap, tmp_bitmap); | |
| } else { | |
| rc = SLURM_ERROR; | |
| error("%s: invalid block or switch specified in hostlist function: \"%s\"", | |
| __func__, node_str); | |
| } | |
| } else if (!xstrncmp("feature{", node_str, 8)) { | |
| add_nodes_with_feature_to_bitmap(node_bitmap, start_ptr); | |
| } else { | |
| rc = SLURM_ERROR; | |
| error("Invalid hostlist_function specified: %s", node_str); | |
| } | |
| *end_ptr = '}'; | |
| return rc; | |
| } | |
| extern int parse_hostlist_functions(hostlist_t **hostlist) | |
| { | |
| hostlist_t *new_hostlist = hostlist_create(NULL); | |
| node_record_t *node_ptr; | |
| char *host; | |
| int rc = SLURM_SUCCESS; | |
| while ((host = hostlist_shift(*hostlist))) { | |
| if ((strchr(host, '{'))) { | |
| bitstr_t *node_bitmap = bit_alloc(node_record_count); | |
| if (_parse_hostlist_function(node_bitmap, host)) { | |
| rc = SLURM_ERROR; | |
| } else { | |
| for (int i = 0; (node_ptr = next_node_bitmap( | |
| node_bitmap, &i)); | |
| i++) { | |
| hostlist_push_host(new_hostlist, | |
| node_ptr->name); | |
| } | |
| } | |
| FREE_NULL_BITMAP(node_bitmap); | |
| } else { | |
| hostlist_push_host(new_hostlist, host); | |
| } | |
| xfree(host); | |
| } | |
| hostlist_destroy(*hostlist); | |
| *hostlist = new_hostlist; | |
| return rc; | |
| } | |
| static int _single_node_name2bitmap(char *node_name, bool test_alias, | |
| bitstr_t *bitmap, | |
| hostlist_t **invalid_hostlist) | |
| { | |
| int rc = SLURM_SUCCESS; | |
| /* If there are curly brackets it must be a hostlist_function */ | |
| if ((xstrchr(node_name, '{'))) { | |
| rc = _parse_hostlist_function(bitmap, node_name); | |
| } else { | |
| node_record_t *node_ptr; | |
| node_ptr = _find_node_record(node_name, test_alias, true); | |
| if (node_ptr) | |
| bit_set(bitmap, node_ptr->index); | |
| else | |
| rc = SLURM_ERROR; | |
| } | |
| if ((rc != SLURM_SUCCESS) && invalid_hostlist) { | |
| debug2("%s: invalid node specified: \"%s\"", | |
| __func__, node_name); | |
| if (*invalid_hostlist) { | |
| hostlist_push_host(*invalid_hostlist, node_name); | |
| } else { | |
| *invalid_hostlist = hostlist_create(node_name); | |
| } | |
| rc = SLURM_SUCCESS; | |
| } else if (rc != SLURM_SUCCESS) { | |
| error("%s: invalid node specified: \"%s\"", | |
| __func__, node_name); | |
| rc = EINVAL; | |
| } | |
| return rc; | |
| } | |
| /* | |
| * node_name2bitmap - given a node name regular expression, build a bitmap | |
| * representation | |
| * IN node_names - list of nodes | |
| * IN: test_alias - if set, also test NodeHostName value | |
| * OUT bitmap - set to bitmap, may not have all bits set on error | |
| * RET 0 if no error, otherwise EINVAL | |
| * NOTE: call FREE_NULL_BITMAP() to free bitmap memory when no longer required | |
| */ | |
| extern int node_name2bitmap(char *node_names, bool test_alias, | |
| bitstr_t **bitmap, hostlist_t **invalid_hostlist) | |
| { | |
| int rc = SLURM_SUCCESS; | |
| char *this_node_name; | |
| hostlist_t *host_list; | |
| *bitmap = bit_alloc(node_record_count); | |
| if (!node_names) { | |
| info("%s: node_names is NULL", __func__); | |
| return rc; | |
| } | |
| if (!(host_list = hostlist_create(node_names))) { | |
| /* likely a badly formatted hostlist */ | |
| error("hostlist_create on %s error:", node_names); | |
| return EINVAL; | |
| } | |
| while ((this_node_name = hostlist_shift(host_list))) { | |
| int tmp_rc; | |
| if ((tmp_rc = _single_node_name2bitmap(this_node_name, | |
| test_alias, *bitmap, | |
| invalid_hostlist))) | |
| rc = tmp_rc; | |
| xfree(this_node_name); | |
| } | |
| hostlist_destroy(host_list); | |
| return rc; | |
| } | |
| /* | |
| * hostlist2bitmap - given a hostlist, build a bitmap representation | |
| * IN hl - hostlist | |
| * IN: test_alias - if set, also test NodeHostName value | |
| * OUT bitmap - set to bitmap, may not have all bits set on error | |
| * RET 0 if no error, otherwise EINVAL | |
| */ | |
| extern int hostlist2bitmap(hostlist_t *hl, bool test_alias, bitstr_t **bitmap) | |
| { | |
| int rc = SLURM_SUCCESS; | |
| bitstr_t *my_bitmap; | |
| char *name; | |
| hostlist_iterator_t *hi; | |
| FREE_NULL_BITMAP(*bitmap); | |
| my_bitmap = bit_alloc(node_record_count); | |
| *bitmap = my_bitmap; | |
| hi = hostlist_iterator_create(hl); | |
| while ((name = hostlist_next(hi))) { | |
| int tmp_rc; | |
| if ((tmp_rc = _single_node_name2bitmap(name, test_alias, | |
| *bitmap, NULL))) | |
| rc = tmp_rc; | |
| xfree(name); | |
| } | |
| hostlist_iterator_destroy(hi); | |
| return rc; | |
| } | |
| /* Only delete config_ptr if isn't referenced by another node. */ | |
| static void _delete_node_config_ptr(node_record_t *node_ptr) | |
| { | |
| bool delete = true; | |
| node_record_t *tmp_ptr; | |
| config_record_t *this_config_ptr; | |
| if (!node_ptr->config_ptr) | |
| return; | |
| /* clear in case config_ptr is still referenced by other nodes */ | |
| if (node_ptr->config_ptr->node_bitmap) | |
| bit_clear(node_ptr->config_ptr->node_bitmap, node_ptr->index); | |
| this_config_ptr = node_ptr->config_ptr; | |
| node_ptr->config_ptr = NULL; | |
| for (int i = 0; (tmp_ptr = next_node(&i)); i++) { | |
| if (tmp_ptr->config_ptr == this_config_ptr) { | |
| delete = false; | |
| break; | |
| } | |
| } | |
| if (delete) | |
| list_delete_ptr(config_list, this_config_ptr); | |
| } | |
| extern bool node_conf_overrides(node_record_t *node_ptr) | |
| { | |
| if (slurm_conf.conf_flags & CONF_FLAG_OR) | |
| return true; | |
| /* This also matches documented "config_overrides" */ | |
| return xstrcasestr(node_ptr->parameters, "config_override"); | |
| } | |
| /* Purge the contents of a node record */ | |
| extern void purge_node_rec(void *in) | |
| { | |
| node_record_t *node_ptr = in; | |
| xfree(node_ptr->arch); | |
| xfree(node_ptr->cert_token); | |
| xfree(node_ptr->comment); | |
| FREE_NULL_DATA(node_ptr->kv); | |
| xfree(node_ptr->comm_name); | |
| FREE_NULL_BITMAP(node_ptr->core_drain_bitmap); | |
| FREE_NULL_LIST(node_ptr->cpu_drain_groups); | |
| xfree(node_ptr->cpu_spec_list); | |
| xfree(node_ptr->extra); | |
| FREE_NULL_DATA(node_ptr->extra_data); | |
| xfree(node_ptr->features); | |
| xfree(node_ptr->features_act); | |
| xfree(node_ptr->gpu_spec); | |
| FREE_NULL_BITMAP(node_ptr->gpu_spec_bitmap); | |
| xfree(node_ptr->gres); | |
| FREE_NULL_LIST(node_ptr->gres_list); | |
| FREE_NULL_LIST(node_ptr->hres_list); | |
| FREE_NULL_LIST(node_ptr->hres_list_config); | |
| xfree(node_ptr->instance_id); | |
| xfree(node_ptr->instance_type); | |
| xfree(node_ptr->max_drained_tres); | |
| xfree(node_ptr->mcs_label); | |
| drained_tres_count_clear(&node_ptr->mem_drain); | |
| xfree(node_ptr->name); | |
| xfree(node_ptr->node_hostname); | |
| FREE_NULL_BITMAP(node_ptr->node_spec_bitmap); | |
| xfree(node_ptr->os); | |
| xfree(node_ptr->parameters); | |
| xfree(node_ptr->part_pptr); | |
| xfree(node_ptr->reason); | |
| xfree(node_ptr->resv_name); | |
| xfree(node_ptr->version); | |
| acct_gather_energy_destroy(node_ptr->energy); | |
| xfree(node_ptr->topology_str); | |
| xfree(node_ptr->topology_orig_str); | |
| xfree(node_ptr->tres_str); | |
| xfree(node_ptr->tres_fmt_str); | |
| xfree(node_ptr->tres_cnt); | |
| xfree(node_ptr); | |
| } | |
| extern bool node_conf_has_drained_tres(node_record_t *node_ptr) | |
| { | |
| if (node_ptr->mem_drain.count) | |
| return true; | |
| if (node_ptr->core_drain_bitmap && | |
| bit_set_count(node_ptr->core_drain_bitmap)) | |
| return true; | |
| if (gres_node_state_list_has_drain_gres(node_ptr->gres_list)) | |
| return true; | |
| return false; | |
| } | |
| extern bool node_conf_set_degraded_flag(node_record_t *node_ptr) | |
| { | |
| bool degraded = node_conf_has_drained_tres(node_ptr); | |
| if (degraded) | |
| node_ptr->node_state |= NODE_STATE_DEGRADED; | |
| else | |
| node_ptr->node_state &= (~NODE_STATE_DEGRADED); | |
| return degraded; | |
| } | |
| extern bool node_conf_resume_all_drained_tres(node_record_t *node_ptr) | |
| { | |
| bool did_change = false; | |
| if (node_ptr->core_drain_bitmap && | |
| bit_set_count(node_ptr->core_drain_bitmap)) | |
| did_change = true; | |
| /* Drop the map and the reasons together, as a per-core resume does. */ | |
| FREE_NULL_BITMAP(node_ptr->core_drain_bitmap); | |
| FREE_NULL_LIST(node_ptr->cpu_drain_groups); | |
| if (node_ptr->mem_drain.count) { | |
| drained_tres_count_clear(&node_ptr->mem_drain); | |
| did_change = true; | |
| } | |
| if (gres_node_state_list_resume_drain(node_ptr->gres_list)) | |
| did_change = true; | |
| return did_change; | |
| } | |
| /* | |
| * rehash_node - build a hash table of the node_record entries. | |
| * NOTE: using xhash implementation | |
| */ | |
| extern void rehash_node(void) | |
| { | |
| int i; | |
| node_record_t *node_ptr; | |
| xhash_free (node_hash_table); | |
| node_hash_table = xhash_init(_node_record_hash_identity, NULL); | |
| for (i = 0; (node_ptr = next_node(&i)); i++) { | |
| if ((node_ptr->name == NULL) || | |
| (node_ptr->name[0] == '\0')) | |
| continue; /* vestigial record */ | |
| xhash_add(node_hash_table, node_ptr); | |
| } | |
| #if _DEBUG | |
| _dump_hash(); | |
| #endif | |
| return; | |
| } | |
| /* Convert a node state string to it's equivalent enum value */ | |
| extern int state_str2int(const char *state_str, char *node_name) | |
| { | |
| int state_val = NO_VAL; | |
| int i; | |
| for (i = 0; i <= NODE_STATE_END; i++) { | |
| if (xstrcasecmp(node_state_string(i), "END") == 0) | |
| break; | |
| if (xstrcasecmp(node_state_string(i), state_str) == 0) { | |
| state_val = i; | |
| break; | |
| } | |
| } | |
| if (i >= NODE_STATE_END) { | |
| if (xstrncasecmp("CLOUD", state_str, 5) == 0) | |
| state_val = NODE_STATE_IDLE | NODE_STATE_CLOUD | | |
| NODE_STATE_POWERED_DOWN; | |
| else if (!xstrncasecmp("EXTERNAL", state_str, 8)) | |
| state_val = NODE_STATE_IDLE | NODE_STATE_EXTERNAL; | |
| else if (xstrncasecmp("DRAIN", state_str, 5) == 0) | |
| state_val = NODE_STATE_UNKNOWN | NODE_STATE_DRAIN; | |
| else if (xstrncasecmp("FAIL", state_str, 4) == 0) | |
| state_val = NODE_STATE_IDLE | NODE_STATE_FAIL; | |
| } | |
| if (state_val == NO_VAL) { | |
| error("node %s has invalid state %s", node_name, state_str); | |
| errno = EINVAL; | |
| } | |
| return state_val; | |
| } | |
| /* (re)set cr_node_num_cores arrays */ | |
| extern void cr_init_global_core_data(node_record_t **node_ptr, int node_cnt) | |
| { | |
| uint32_t n; | |
| int prev_index = 0; | |
| cr_fini_global_core_data(); | |
| cr_node_num_cores = xcalloc(node_cnt, sizeof(uint16_t)); | |
| cr_node_cores_offset = xcalloc(node_cnt + 1, sizeof(uint32_t)); | |
| for (n = 0; n < node_cnt; n++) { | |
| if (!node_ptr[n]) | |
| continue; | |
| cr_node_num_cores[n] = node_ptr[n]->tot_cores; | |
| if (n > 0) { | |
| cr_node_cores_offset[n] = | |
| cr_node_cores_offset[prev_index] + | |
| cr_node_num_cores[prev_index] ; | |
| prev_index = n; | |
| } else | |
| cr_node_cores_offset[0] = 0; | |
| } | |
| /* an extra value is added to get the total number of cores */ | |
| /* as cr_get_coremap_offset is sometimes used to get the total */ | |
| /* number of cores in the cluster */ | |
| cr_node_cores_offset[node_cnt] = cr_node_cores_offset[prev_index] + | |
| cr_node_num_cores[prev_index] ; | |
| } | |
| extern void cr_fini_global_core_data(void) | |
| { | |
| xfree(cr_node_num_cores); | |
| xfree(cr_node_cores_offset); | |
| } | |
| /* | |
| * Return the coremap index to the first core of the given node | |
| * | |
| * If node_index points to NULL record in the node_record_table_ptr, then it | |
| * will attempt to find the next available node. If a valid node isn't found, | |
| * then the last core offset will be returned -- | |
| * cr_node_cores_offset[node_record_count]. | |
| */ | |
| extern uint32_t cr_get_coremap_offset(uint32_t node_index) | |
| { | |
| xassert(cr_node_cores_offset); | |
| if (next_node((int *)&node_index)) | |
| return cr_node_cores_offset[node_index]; | |
| return cr_node_cores_offset[node_record_count]; | |
| } | |
| /* | |
| * Determine maximum number of CPUs on this node usable by a job | |
| * ntasks_per_core IN - tasks-per-core to be launched by this job | |
| * cpus_per_task IN - number of required CPUs per task for this job | |
| * total_cores IN - total number of cores on this node | |
| * total_cpus IN - total number of CPUs on this node | |
| * RET count of usable CPUs on this node usable by this job | |
| */ | |
| extern int adjust_cpus_nppcu(uint16_t ntasks_per_core, int cpus_per_task, | |
| int total_cores, int total_cpus) | |
| { | |
| int cpus = total_cpus; | |
| //FIXME: This function ignores tasks-per-socket and tasks-per-node checks. | |
| // Those parameters are tested later | |
| if ((ntasks_per_core != 0) && (ntasks_per_core != 0xffff) && | |
| (cpus_per_task != 0)) { | |
| cpus = MAX((total_cores * ntasks_per_core * cpus_per_task), | |
| total_cpus); | |
| } | |
| return cpus; | |
| } | |
| extern char *find_hostname(uint32_t pos, char *hosts) | |
| { | |
| hostlist_t *hostlist = NULL; | |
| char *host = NULL; | |
| if (!hosts || (pos == NO_VAL) || (pos == INFINITE)) | |
| return NULL; | |
| hostlist = hostlist_create(hosts); | |
| host = hostlist_nth(hostlist, pos); | |
| hostlist_destroy(hostlist); | |
| return host; | |
| } | |
| extern node_record_t *next_node(int *index) | |
| { | |
| xassert(index); | |
| if (!node_record_table_ptr || | |
| (*index >= node_record_count)) | |
| return NULL; | |
| while (!node_record_table_ptr[*index]) { | |
| (*index)++; | |
| if (*index >= node_record_count) | |
| return NULL; | |
| if (*index > last_node_index) | |
| return NULL; | |
| } | |
| xassert(node_record_table_ptr[*index]->index == *index); | |
| return node_record_table_ptr[*index]; | |
| } | |
| extern node_record_t *next_node_bitmap(bitstr_t *bitmap, int *index) | |
| { | |
| xassert(index); | |
| if (!node_record_table_ptr || | |
| (*index >= node_record_count)) | |
| return NULL; | |
| xassert(bitmap); | |
| xassert(bit_size(bitmap) == node_record_count); | |
| while (true) { | |
| *index = bit_ffs_from_bit(bitmap, *index); | |
| if (*index == -1) | |
| return NULL; | |
| if (node_record_table_ptr[*index]) | |
| break; | |
| (*index)++; /* Skip blank entries */ | |
| } | |
| xassert(node_record_table_ptr[*index]->index == *index); | |
| return node_record_table_ptr[*index]; | |
| } | |
| extern bitstr_t *node_conf_get_active_bitmap(void) | |
| { | |
| bitstr_t *b = bit_alloc(node_record_count); | |
| node_conf_set_all_active_bits(b); | |
| return b; | |
| } | |
| extern void node_conf_set_all_active_bits(bitstr_t *b) | |
| { | |
| for (int i = 0; next_node(&i); i++) | |
| bit_set(b, i); | |
| } | |
| extern char *node_conf_nodestr_tokenize(char *s, char **save_ptr) | |
| { | |
| char *end; | |
| if (s == NULL) | |
| s = *save_ptr; | |
| xassert(s); /* If s is NULL here we are using this function wrong */ | |
| if (*s == '\0') { | |
| *save_ptr = s; | |
| return NULL; | |
| } | |
| /* token ends with a comma not followed by a digit */ | |
| end = s; | |
| while (*end && ((end[0] != ',') || isdigit(end[1]))) | |
| end++; | |
| if (*end) | |
| *end++ = '\0'; | |
| *save_ptr = end; | |
| return s; | |
| } | |
| extern void node_conf_create_cluster_core_bitmap(bitstr_t **core_bitmap) | |
| { | |
| if (*core_bitmap) | |
| return; | |
| *core_bitmap = bit_alloc(cr_get_coremap_offset(node_record_count)); | |
| } | |
| /* | |
| * Pack node_record_t to buffer. | |
| * | |
| * Used for dumping node state and passing node_record_t between ctld and | |
| * stepmgr. | |
| */ | |
| static void _node_record_pack(void *in, uint16_t protocol_version, | |
| buf_t *buffer, bool pack_secrets) | |
| { | |
| node_record_t *object = in; | |
| if (protocol_version >= SLURM_26_11_PROTOCOL_VERSION) { | |
| pack_time(object->cert_last_renewal, buffer); | |
| if (pack_secrets) | |
| packstr(object->cert_token, buffer); | |
| else if (object->cert_token) | |
| packstr("set", buffer); | |
| else | |
| packnull(buffer); | |
| packstr(object->comm_name, buffer); | |
| packstr(object->name, buffer); | |
| packstr(object->node_hostname, buffer); | |
| packstr(object->comment, buffer); | |
| if (pack_data(object->kv, protocol_version, buffer)) | |
| fatal_abort("%s: Invalid KV data", __func__); | |
| packstr(object->extra, buffer); | |
| packstr(object->reason, buffer); | |
| packstr(object->features, buffer); | |
| packstr(object->features_act, buffer); | |
| packstr(object->gres, buffer); | |
| slurm_pack_list(object->hres_list, slurm_pack_node_hres_info, | |
| buffer, protocol_version); | |
| slurm_pack_list(object->hres_list_config, | |
| slurm_pack_node_hres_info, buffer, | |
| protocol_version); | |
| packstr(object->instance_id, buffer); | |
| packstr(object->instance_type, buffer); | |
| packstr(object->cpu_spec_list, buffer); | |
| pack32(object->next_state, buffer); | |
| pack32(object->node_state, buffer); | |
| pack32(object->cpu_bind, buffer); | |
| pack32(object->config_ptr ? object->config_ptr->suspend_time : | |
| NO_VAL, | |
| buffer); | |
| pack16(object->cpus, buffer); | |
| pack16(object->boards, buffer); | |
| pack16(object->tot_cores, buffer); | |
| pack16(object->tot_sockets, buffer); | |
| pack16(object->cores, buffer); | |
| pack16(object->core_spec_cnt, buffer); | |
| pack64(object->mem_spec_limit, buffer); | |
| pack16(object->threads, buffer); | |
| pack64(object->real_memory, buffer); | |
| pack16(object->res_cores_per_gpu, buffer); | |
| pack_bit_str_hex(object->gpu_spec_bitmap, buffer); | |
| pack32(object->tmp_disk, buffer); | |
| packstr(object->topology_str, buffer); | |
| pack32(object->reason_uid, buffer); | |
| pack_time(object->reason_time, buffer); | |
| pack_time(object->resume_after, buffer); | |
| pack_time(object->boot_req_time, buffer); | |
| pack_time(object->power_save_req_time, buffer); | |
| pack_time(object->last_busy, buffer); | |
| pack_time(object->last_response, buffer); | |
| packstr(object->parameters, buffer); | |
| pack16(object->port, buffer); | |
| packstr(object->power_action_name, buffer); | |
| pack16(object->protocol_version, buffer); | |
| pack16(object->tpc, buffer); | |
| packstr(object->mcs_label, buffer); | |
| (void) gres_node_state_pack(object->gres_list, buffer, | |
| protocol_version); | |
| pack32(object->weight, buffer); | |
| pack_bit_str_hex(object->core_drain_bitmap, buffer); | |
| drained_tres_groups_pack(object->cpu_drain_groups, | |
| protocol_version, buffer); | |
| if (object->config_ptr && object->config_ptr->max_drained_tres) | |
| packstr(object->config_ptr->max_drained_tres, buffer); | |
| else | |
| packstr(object->max_drained_tres, buffer); | |
| drained_tres_count_pack(&object->mem_drain, protocol_version, | |
| buffer); | |
| pack64(object->boot_id, buffer); | |
| } else if (protocol_version >= SLURM_26_05_PROTOCOL_VERSION) { | |
| pack_time(object->cert_last_renewal, buffer); | |
| if (pack_secrets) | |
| packstr(object->cert_token, buffer); | |
| else if (object->cert_token) | |
| packstr("set", buffer); | |
| else | |
| packnull(buffer); | |
| packstr(object->comm_name, buffer); | |
| packstr(object->name, buffer); | |
| packstr(object->node_hostname, buffer); | |
| packstr(object->comment, buffer); | |
| packstr(object->extra, buffer); | |
| packstr(object->reason, buffer); | |
| packstr(object->features, buffer); | |
| packstr(object->features_act, buffer); | |
| packstr(object->gres, buffer); | |
| packstr(object->instance_id, buffer); | |
| packstr(object->instance_type, buffer); | |
| packstr(object->cpu_spec_list, buffer); | |
| pack32(object->next_state, buffer); | |
| pack32(object->node_state, buffer); | |
| pack32(object->cpu_bind, buffer); | |
| pack32(object->suspend_time, buffer); | |
| pack16(object->cpus, buffer); | |
| pack16(object->boards, buffer); | |
| pack16(object->tot_cores, buffer); | |
| pack16(object->tot_sockets, buffer); | |
| pack16(object->cores, buffer); | |
| pack16(object->core_spec_cnt, buffer); | |
| pack64(object->mem_spec_limit, buffer); | |
| pack16(object->threads, buffer); | |
| pack64(object->real_memory, buffer); | |
| pack16(object->res_cores_per_gpu, buffer); | |
| pack_bit_str_hex(object->gpu_spec_bitmap, buffer); | |
| pack32(object->tmp_disk, buffer); | |
| packstr(object->topology_str, buffer); | |
| pack32(object->reason_uid, buffer); | |
| pack_time(object->reason_time, buffer); | |
| pack_time(object->resume_after, buffer); | |
| pack_time(object->boot_req_time, buffer); | |
| pack_time(object->power_save_req_time, buffer); | |
| pack_time(object->last_busy, buffer); | |
| pack_time(object->last_response, buffer); | |
| packstr(object->parameters, buffer); | |
| pack16(object->port, buffer); | |
| packstr(object->power_action_name, buffer); | |
| pack16(object->protocol_version, buffer); | |
| pack16(object->tpc, buffer); | |
| packstr(object->mcs_label, buffer); | |
| (void) gres_node_state_pack(object->gres_list, buffer, | |
| protocol_version); | |
| pack32(object->weight, buffer); | |
| } else if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) { | |
| pack_time(object->cert_last_renewal, buffer); | |
| if (pack_secrets) | |
| packstr(object->cert_token, buffer); | |
| else if (object->cert_token) | |
| packstr("set", buffer); | |
| else | |
| packnull(buffer); | |
| packstr(object->comm_name, buffer); | |
| packstr(object->name, buffer); | |
| packstr(object->node_hostname, buffer); | |
| packstr(object->comment, buffer); | |
| packstr(object->extra, buffer); | |
| packstr(object->reason, buffer); | |
| packstr(object->features, buffer); | |
| packstr(object->features_act, buffer); | |
| packstr(object->gres, buffer); | |
| packstr(object->instance_id, buffer); | |
| packstr(object->instance_type, buffer); | |
| packstr(object->cpu_spec_list, buffer); | |
| pack32(object->next_state, buffer); | |
| pack32(object->node_state, buffer); | |
| pack32(object->cpu_bind, buffer); | |
| pack16(object->cpus, buffer); | |
| pack16(object->boards, buffer); | |
| pack16(object->tot_cores, buffer); | |
| pack16(object->tot_sockets, buffer); | |
| pack16(object->cores, buffer); | |
| pack16(object->core_spec_cnt, buffer); | |
| pack64(object->mem_spec_limit, buffer); | |
| pack16(object->threads, buffer); | |
| pack64(object->real_memory, buffer); | |
| pack16(object->res_cores_per_gpu, buffer); | |
| pack_bit_str_hex(object->gpu_spec_bitmap, buffer); | |
| pack32(object->tmp_disk, buffer); | |
| packstr(object->topology_str, buffer); | |
| packstr(object->topology_orig_str, buffer); | |
| pack32(object->reason_uid, buffer); | |
| pack_time(object->reason_time, buffer); | |
| pack_time(object->resume_after, buffer); | |
| pack_time(object->boot_req_time, buffer); | |
| pack_time(object->power_save_req_time, buffer); | |
| pack_time(object->last_busy, buffer); | |
| pack_time(object->last_response, buffer); | |
| packstr(object->parameters, buffer); | |
| pack16(object->port, buffer); | |
| pack16(object->protocol_version, buffer); | |
| pack16(object->tpc, buffer); | |
| packstr(object->mcs_label, buffer); | |
| (void) gres_node_state_pack(object->gres_list, buffer, | |
| protocol_version); | |
| pack32(object->weight, buffer); | |
| } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) { | |
| pack_time(object->cert_last_renewal, buffer); | |
| if (pack_secrets) | |
| packstr(object->cert_token, buffer); | |
| else if (object->cert_token) | |
| packstr("set", buffer); | |
| else | |
| packnull(buffer); | |
| packstr(object->comm_name, buffer); | |
| packstr(object->name, buffer); | |
| packstr(object->node_hostname, buffer); | |
| packstr(object->comment, buffer); | |
| packstr(object->extra, buffer); | |
| packstr(object->reason, buffer); | |
| packstr(object->features, buffer); | |
| packstr(object->features_act, buffer); | |
| packstr(object->gres, buffer); | |
| packstr(object->instance_id, buffer); | |
| packstr(object->instance_type, buffer); | |
| packstr(object->cpu_spec_list, buffer); | |
| pack32(object->next_state, buffer); | |
| pack32(object->node_state, buffer); | |
| pack32(object->cpu_bind, buffer); | |
| pack16(object->cpus, buffer); | |
| pack16(object->boards, buffer); | |
| pack16(object->tot_cores, buffer); | |
| pack16(object->tot_sockets, buffer); | |
| pack16(object->cores, buffer); | |
| pack16(object->core_spec_cnt, buffer); | |
| pack64(object->mem_spec_limit, buffer); | |
| pack16(object->threads, buffer); | |
| pack64(object->real_memory, buffer); | |
| pack16(object->res_cores_per_gpu, buffer); | |
| pack_bit_str_hex(object->gpu_spec_bitmap, buffer); | |
| pack32(object->tmp_disk, buffer); | |
| packstr(object->topology_str, buffer); | |
| pack32(object->reason_uid, buffer); | |
| pack_time(object->reason_time, buffer); | |
| pack_time(object->resume_after, buffer); | |
| pack_time(object->boot_req_time, buffer); | |
| pack_time(object->power_save_req_time, buffer); | |
| pack_time(object->last_busy, buffer); | |
| pack_time(object->last_response, buffer); | |
| pack16(object->port, buffer); | |
| pack16(object->protocol_version, buffer); | |
| pack16(object->tpc, buffer); | |
| packstr(object->mcs_label, buffer); | |
| (void) gres_node_state_pack(object->gres_list, buffer, | |
| protocol_version); | |
| pack32(object->weight, buffer); | |
| } | |
| } | |
| extern void node_record_pack(void *in, uint16_t protocol_version, | |
| buf_t *buffer) | |
| { | |
| _node_record_pack(in, protocol_version, buffer, false); | |
| } | |
| extern void node_record_pack_state(void *in, uint16_t protocol_version, | |
| buf_t *buffer) | |
| { | |
| _node_record_pack(in, protocol_version, buffer, true); | |
| } | |
| extern int node_record_unpack(void **out, | |
| uint16_t protocol_version, | |
| buf_t *buffer) | |
| { | |
| node_record_t *object = xmalloc(sizeof(*object)); | |
| object->magic = NODE_MAGIC; | |
| object->suspend_time_configured = NO_VAL; | |
| *out = object; | |
| if (protocol_version >= SLURM_26_11_PROTOCOL_VERSION) { | |
| safe_unpack_time(&object->cert_last_renewal, buffer); | |
| safe_unpackstr(&object->cert_token, buffer); | |
| safe_unpackstr(&object->comm_name, buffer); | |
| safe_unpackstr(&object->name, buffer); | |
| safe_unpackstr(&object->node_hostname, buffer); | |
| safe_unpackstr(&object->comment, buffer); | |
| object->kv = data_new(); | |
| safe_unpack_data(object->kv, protocol_version, buffer); | |
| safe_unpackstr(&object->extra, buffer); | |
| safe_unpackstr(&object->reason, buffer); | |
| safe_unpackstr(&object->features, buffer); | |
| safe_unpackstr(&object->features_act, buffer); | |
| safe_unpackstr(&object->gres, buffer); | |
| if (slurm_unpack_list(&object->hres_list, | |
| slurm_unpack_node_hres_info, | |
| slurm_free_node_hres_info, buffer, | |
| protocol_version)) | |
| goto unpack_error; | |
| if (slurm_unpack_list(&object->hres_list_config, | |
| slurm_unpack_node_hres_info, | |
| slurm_free_node_hres_info, buffer, | |
| protocol_version)) | |
| goto unpack_error; | |
| safe_unpackstr(&object->instance_id, buffer); | |
| safe_unpackstr(&object->instance_type, buffer); | |
| safe_unpackstr(&object->cpu_spec_list, buffer); | |
| safe_unpack32(&object->next_state, buffer); | |
| safe_unpack32(&object->node_state, buffer); | |
| safe_unpack32(&object->cpu_bind, buffer); | |
| safe_unpack32(&object->suspend_time_configured, buffer); | |
| safe_unpack16(&object->cpus, buffer); | |
| safe_unpack16(&object->boards, buffer); | |
| safe_unpack16(&object->tot_cores, buffer); | |
| safe_unpack16(&object->tot_sockets, buffer); | |
| safe_unpack16(&object->cores, buffer); | |
| safe_unpack16(&object->core_spec_cnt, buffer); | |
| safe_unpack64(&object->mem_spec_limit, buffer); | |
| safe_unpack16(&object->threads, buffer); | |
| safe_unpack64(&object->real_memory, buffer); | |
| safe_unpack16(&object->res_cores_per_gpu, buffer); | |
| unpack_bit_str_hex(&object->gpu_spec_bitmap, buffer); | |
| safe_unpack32(&object->tmp_disk, buffer); | |
| safe_unpackstr(&object->topology_str, buffer); | |
| safe_unpack32(&object->reason_uid, buffer); | |
| safe_unpack_time(&object->reason_time, buffer); | |
| safe_unpack_time(&object->resume_after, buffer); | |
| safe_unpack_time(&object->boot_req_time, buffer); | |
| safe_unpack_time(&object->power_save_req_time, buffer); | |
| safe_unpack_time(&object->last_busy, buffer); | |
| safe_unpack_time(&object->last_response, buffer); | |
| safe_unpackstr(&object->parameters, buffer); | |
| safe_unpack16(&object->port, buffer); | |
| safe_unpackstr(&object->power_action_name, buffer); | |
| safe_unpack16(&object->protocol_version, buffer); | |
| safe_unpack16(&object->tpc, buffer); | |
| safe_unpackstr(&object->mcs_label, buffer); | |
| if (gres_node_state_unpack(&object->gres_list, buffer, | |
| object->name, | |
| protocol_version) != SLURM_SUCCESS) | |
| goto unpack_error; | |
| safe_unpack32(&object->weight, buffer); | |
| unpack_bit_str_hex(&object->core_drain_bitmap, buffer); | |
| if (drained_tres_groups_unpack(&object->cpu_drain_groups, | |
| protocol_version, | |
| buffer) != SLURM_SUCCESS) | |
| goto unpack_error; | |
| safe_unpackstr(&object->max_drained_tres, buffer); | |
| if (drained_tres_count_unpack(&object->mem_drain, | |
| protocol_version, | |
| buffer) != SLURM_SUCCESS) | |
| goto unpack_error; | |
| safe_unpack64(&object->boot_id, buffer); | |
| } else if (protocol_version >= SLURM_26_05_PROTOCOL_VERSION) { | |
| safe_unpack_time(&object->cert_last_renewal, buffer); | |
| safe_unpackstr(&object->cert_token, buffer); | |
| safe_unpackstr(&object->comm_name, buffer); | |
| safe_unpackstr(&object->name, buffer); | |
| safe_unpackstr(&object->node_hostname, buffer); | |
| safe_unpackstr(&object->comment, buffer); | |
| safe_unpackstr(&object->extra, buffer); | |
| safe_unpackstr(&object->reason, buffer); | |
| safe_unpackstr(&object->features, buffer); | |
| safe_unpackstr(&object->features_act, buffer); | |
| safe_unpackstr(&object->gres, buffer); | |
| safe_unpackstr(&object->instance_id, buffer); | |
| safe_unpackstr(&object->instance_type, buffer); | |
| safe_unpackstr(&object->cpu_spec_list, buffer); | |
| safe_unpack32(&object->next_state, buffer); | |
| safe_unpack32(&object->node_state, buffer); | |
| safe_unpack32(&object->cpu_bind, buffer); | |
| safe_unpack32(&object->suspend_time, buffer); | |
| /* Older state files have no separate configured value. */ | |
| object->suspend_time_configured = object->suspend_time; | |
| safe_unpack16(&object->cpus, buffer); | |
| safe_unpack16(&object->boards, buffer); | |
| safe_unpack16(&object->tot_cores, buffer); | |
| safe_unpack16(&object->tot_sockets, buffer); | |
| safe_unpack16(&object->cores, buffer); | |
| safe_unpack16(&object->core_spec_cnt, buffer); | |
| safe_unpack64(&object->mem_spec_limit, buffer); | |
| safe_unpack16(&object->threads, buffer); | |
| safe_unpack64(&object->real_memory, buffer); | |
| safe_unpack16(&object->res_cores_per_gpu, buffer); | |
| unpack_bit_str_hex(&object->gpu_spec_bitmap, buffer); | |
| safe_unpack32(&object->tmp_disk, buffer); | |
| safe_unpackstr(&object->topology_str, buffer); | |
| safe_unpack32(&object->reason_uid, buffer); | |
| safe_unpack_time(&object->reason_time, buffer); | |
| safe_unpack_time(&object->resume_after, buffer); | |
| safe_unpack_time(&object->boot_req_time, buffer); | |
| safe_unpack_time(&object->power_save_req_time, buffer); | |
| safe_unpack_time(&object->last_busy, buffer); | |
| safe_unpack_time(&object->last_response, buffer); | |
| safe_unpackstr(&object->parameters, buffer); | |
| safe_unpack16(&object->port, buffer); | |
| safe_unpackstr(&object->power_action_name, buffer); | |
| safe_unpack16(&object->protocol_version, buffer); | |
| safe_unpack16(&object->tpc, buffer); | |
| safe_unpackstr(&object->mcs_label, buffer); | |
| if (gres_node_state_unpack(&object->gres_list, buffer, | |
| object->name, | |
| protocol_version) != SLURM_SUCCESS) | |
| goto unpack_error; | |
| safe_unpack32(&object->weight, buffer); | |
| } else if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) { | |
| safe_unpack_time(&object->cert_last_renewal, buffer); | |
| safe_unpackstr(&object->cert_token, buffer); | |
| safe_unpackstr(&object->comm_name, buffer); | |
| safe_unpackstr(&object->name, buffer); | |
| safe_unpackstr(&object->node_hostname, buffer); | |
| safe_unpackstr(&object->comment, buffer); | |
| safe_unpackstr(&object->extra, buffer); | |
| safe_unpackstr(&object->reason, buffer); | |
| safe_unpackstr(&object->features, buffer); | |
| safe_unpackstr(&object->features_act, buffer); | |
| safe_unpackstr(&object->gres, buffer); | |
| safe_unpackstr(&object->instance_id, buffer); | |
| safe_unpackstr(&object->instance_type, buffer); | |
| safe_unpackstr(&object->cpu_spec_list, buffer); | |
| safe_unpack32(&object->next_state, buffer); | |
| safe_unpack32(&object->node_state, buffer); | |
| safe_unpack32(&object->cpu_bind, buffer); | |
| safe_unpack16(&object->cpus, buffer); | |
| safe_unpack16(&object->boards, buffer); | |
| safe_unpack16(&object->tot_cores, buffer); | |
| safe_unpack16(&object->tot_sockets, buffer); | |
| safe_unpack16(&object->cores, buffer); | |
| safe_unpack16(&object->core_spec_cnt, buffer); | |
| safe_unpack64(&object->mem_spec_limit, buffer); | |
| safe_unpack16(&object->threads, buffer); | |
| safe_unpack64(&object->real_memory, buffer); | |
| safe_unpack16(&object->res_cores_per_gpu, buffer); | |
| unpack_bit_str_hex(&object->gpu_spec_bitmap, buffer); | |
| safe_unpack32(&object->tmp_disk, buffer); | |
| safe_unpackstr(&object->topology_str, buffer); | |
| safe_unpackstr(&object->topology_orig_str, buffer); | |
| safe_unpack32(&object->reason_uid, buffer); | |
| safe_unpack_time(&object->reason_time, buffer); | |
| safe_unpack_time(&object->resume_after, buffer); | |
| safe_unpack_time(&object->boot_req_time, buffer); | |
| safe_unpack_time(&object->power_save_req_time, buffer); | |
| safe_unpack_time(&object->last_busy, buffer); | |
| safe_unpack_time(&object->last_response, buffer); | |
| safe_unpackstr(&object->parameters, buffer); | |
| safe_unpack16(&object->port, buffer); | |
| safe_unpack16(&object->protocol_version, buffer); | |
| safe_unpack16(&object->tpc, buffer); | |
| safe_unpackstr(&object->mcs_label, buffer); | |
| if (gres_node_state_unpack(&object->gres_list, buffer, | |
| object->name, | |
| protocol_version) != SLURM_SUCCESS) | |
| goto unpack_error; | |
| safe_unpack32(&object->weight, buffer); | |
| } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) { | |
| safe_unpack_time(&object->cert_last_renewal, buffer); | |
| safe_unpackstr(&object->cert_token, buffer); | |
| safe_unpackstr(&object->comm_name, buffer); | |
| safe_unpackstr(&object->name, buffer); | |
| safe_unpackstr(&object->node_hostname, buffer); | |
| safe_unpackstr(&object->comment, buffer); | |
| safe_unpackstr(&object->extra, buffer); | |
| safe_unpackstr(&object->reason, buffer); | |
| safe_unpackstr(&object->features, buffer); | |
| safe_unpackstr(&object->features_act, buffer); | |
| safe_unpackstr(&object->gres, buffer); | |
| safe_unpackstr(&object->instance_id, buffer); | |
| safe_unpackstr(&object->instance_type, buffer); | |
| safe_unpackstr(&object->cpu_spec_list, buffer); | |
| safe_unpack32(&object->next_state, buffer); | |
| safe_unpack32(&object->node_state, buffer); | |
| safe_unpack32(&object->cpu_bind, buffer); | |
| safe_unpack16(&object->cpus, buffer); | |
| safe_unpack16(&object->boards, buffer); | |
| safe_unpack16(&object->tot_cores, buffer); | |
| safe_unpack16(&object->tot_sockets, buffer); | |
| safe_unpack16(&object->cores, buffer); | |
| safe_unpack16(&object->core_spec_cnt, buffer); | |
| safe_unpack64(&object->mem_spec_limit, buffer); | |
| safe_unpack16(&object->threads, buffer); | |
| safe_unpack64(&object->real_memory, buffer); | |
| safe_unpack16(&object->res_cores_per_gpu, buffer); | |
| unpack_bit_str_hex(&object->gpu_spec_bitmap, buffer); | |
| safe_unpack32(&object->tmp_disk, buffer); | |
| safe_unpackstr(&object->topology_str, buffer); | |
| safe_unpack32(&object->reason_uid, buffer); | |
| safe_unpack_time(&object->reason_time, buffer); | |
| safe_unpack_time(&object->resume_after, buffer); | |
| safe_unpack_time(&object->boot_req_time, buffer); | |
| safe_unpack_time(&object->power_save_req_time, buffer); | |
| safe_unpack_time(&object->last_busy, buffer); | |
| safe_unpack_time(&object->last_response, buffer); | |
| safe_unpack16(&object->port, buffer); | |
| safe_unpack16(&object->protocol_version, buffer); | |
| safe_unpack16(&object->tpc, buffer); | |
| safe_unpackstr(&object->mcs_label, buffer); | |
| if (gres_node_state_unpack(&object->gres_list, buffer, | |
| object->name, | |
| protocol_version) != SLURM_SUCCESS) | |
| goto unpack_error; | |
| safe_unpack32(&object->weight, buffer); | |
| } else { | |
| error("%s: protocol_version %hu not supported", | |
| __func__, protocol_version); | |
| goto unpack_error; | |
| } | |
| return SLURM_SUCCESS; | |
| unpack_error: | |
| purge_node_rec(object); | |
| *out = NULL; | |
| return SLURM_ERROR; | |
| } | |
| extern config_record_t *config_record_from_node_record(node_record_t *node_ptr) | |
| { | |
| config_record_t *config_ptr = create_config_record(); | |
| config_ptr->boards = node_ptr->boards; | |
| config_ptr->core_spec_cnt = node_ptr->core_spec_cnt; | |
| config_ptr->mem_spec_limit = node_ptr->mem_spec_limit; | |
| config_ptr->cores = node_ptr->cores; | |
| config_ptr->cpu_spec_list = xstrdup(node_ptr->cpu_spec_list); | |
| config_ptr->cpus = node_ptr->cpus; | |
| config_ptr->feature = xstrdup(node_ptr->features); | |
| config_ptr->gres = xstrdup(node_ptr->gres); | |
| config_ptr->max_drained_tres = xstrdup(node_ptr->max_drained_tres); | |
| config_ptr->node_bitmap = bit_alloc(node_record_count); | |
| config_ptr->nodes = xstrdup(node_ptr->name); | |
| config_ptr->real_memory = node_ptr->real_memory; | |
| config_ptr->res_cores_per_gpu = node_ptr->res_cores_per_gpu; | |
| config_ptr->suspend_time = node_ptr->suspend_time_configured; | |
| config_ptr->threads = node_ptr->threads; | |
| config_ptr->tmp_disk = node_ptr->tmp_disk; | |
| config_ptr->topology_str = xstrdup(node_ptr->topology_str); | |
| config_ptr->tot_sockets = node_ptr->tot_sockets; | |
| config_ptr->weight = node_ptr->weight; | |
| return config_ptr; | |
| } | |
| /* | |
| * list_find_feature - find an entry in the feature list, see list.h for | |
| * documentation | |
| * IN key - is feature name or NULL for all features | |
| * RET 1 if found, 0 otherwise | |
| */ | |
| extern int list_find_feature(void *feature_entry, void *key) | |
| { | |
| node_feature_t *feature_ptr = feature_entry; | |
| if (!key) | |
| return 1; | |
| return !xstrcmp(feature_ptr->name, (char *) key); | |
| } | |
| /* | |
| * cpu_drain_groups is indexed by schedulable core. DrainedTRES and the | |
| * accounting TresReference name abstract CPU ids, so expand each drained core | |
| * to the tpc CPU ids it owns. tpc 1 leaves the indexes unchanged. | |
| */ | |
| static char *_cpu_index_from_cores(bitstr_t *cores, uint16_t tpc) | |
| { | |
| bitstr_t *cpus; | |
| bitoff_t ncores; | |
| char *tmp; | |
| if (!cores) | |
| return NULL; | |
| if (tpc == 1) | |
| return bit_fmt_full(cores); | |
| ncores = bit_size(cores); | |
| cpus = bit_alloc(ncores * tpc); | |
| for (bitoff_t i = 0; i < ncores; i++) { | |
| if (bit_test(cores, i)) | |
| bit_nset(cpus, i * tpc, (i * tpc) + tpc - 1); | |
| } | |
| tmp = bit_fmt_full(cpus); | |
| FREE_NULL_BITMAP(cpus); | |
| return tmp; | |
| } | |
| static int _append_drained_cpu_rec(void *x, void *arg) | |
| { | |
| drain_group_t *group = x; | |
| drained_cpu_fmt_t *fmt = arg; | |
| char *tmp = _cpu_index_from_cores(group->index_bits, fmt->tpc); | |
| drained_tres_rec_add(fmt->recs, "cpu", tmp, NULL, NO_VAL64, | |
| group->reason); | |
| xfree(tmp); | |
| return 0; | |
| } | |
| /* | |
| * Append the node's drained CPUs, one record per reason they were drained | |
| * with. Mirrors what the GRES device path reports, so a node whose CPUs were | |
| * drained at different times reports each reason against the CPUs it applies | |
| * to. CPUs have no UUID, so a record names the index range only. | |
| */ | |
| static void _append_drained_cpu_recs(list_t *recs, node_record_t *node_ptr) | |
| { | |
| drained_cpu_fmt_t fmt = { | |
| .recs = recs, | |
| .tpc = node_ptr->tpc, | |
| }; | |
| list_t *sorted; | |
| if (!node_ptr->cpu_drain_groups) | |
| return; | |
| /* | |
| * Sort a shallow copy rather than the node's own list. This runs with | |
| * only a node read lock held - packing a node for scontrol, and the | |
| * accounting registration sweep - and list_sort() rebuilds the list in | |
| * place, rewinding any iterator a concurrent reader is holding. | |
| */ | |
| sorted = list_shallow_copy(node_ptr->cpu_drain_groups); | |
| list_sort(sorted, drained_tres_sort_groups); | |
| list_for_each(sorted, _append_drained_cpu_rec, &fmt); | |
| FREE_NULL_LIST(sorted); | |
| } | |
| extern list_t *node_conf_get_drained_tres_recs(node_record_t *node_ptr) | |
| { | |
| list_t *recs = gres_get_node_drained_tres_recs(node_ptr->gres_list); | |
| if (!recs) | |
| recs = list_create(drained_tres_delete); | |
| _append_drained_cpu_recs(recs, node_ptr); | |
| if (node_ptr->mem_drain.count) | |
| drained_tres_rec_add(recs, "mem", NULL, NULL, | |
| node_ptr->mem_drain.count, | |
| node_ptr->mem_drain.reason); | |
| if (!list_count(recs)) | |
| FREE_NULL_LIST(recs); | |
| return recs; | |
| } |