| /****************************************************************************\ |
| * statistics.c - functions for sdiag command |
| ***************************************************************************** |
| * Produced at Barcelona Supercomputing Center, December 2011 |
| * Written by Alejandro Lucero <alucero@bsc.es> |
| * |
| * This file is part of Slurm, a resource management program. |
| * For details, see <https://slurm.schedmd.com/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * Slurm is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with Slurm; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdio.h> |
| |
| #include "src/slurmctld/agent.h" |
| #include "src/slurmctld/slurmctld.h" |
| #include "src/common/id_util.h" |
| #include "src/common/list.h" |
| #include "src/common/pack.h" |
| #include "src/common/xstring.h" |
| #include "src/common/slurmdbd_defs.h" |
| |
| #include "src/common/assoc_mgr.h" |
| #include "src/interfaces/gres.h" |
| #include "src/interfaces/select.h" |
| |
| #include "src/slurmctld/locks.h" |
| #include "src/slurmctld/statistics.h" |
| |
| typedef struct foreach_fill_jobs_args { |
| int gpu_tres_pos; |
| jobs_stats_t *js; |
| } foreach_fill_jobs_args_t; |
| |
| typedef struct foreach_part_gen_stats { |
| jobs_stats_t *js; |
| nodes_stats_t *ns; |
| partitions_stats_t *ps; |
| } foreach_part_gen_stats_t; |
| |
| /* Pack all scheduling statistics */ |
| extern buf_t *pack_all_stat(uint16_t protocol_version) |
| { |
| buf_t *buffer; |
| int agent_queue_size; |
| int agent_count; |
| int agent_thread_count; |
| int slurmdbd_queue_size = 0; |
| time_t now = time(NULL); |
| |
| if (acct_storage_g_get_data(acct_db_conn, ACCT_STORAGE_INFO_AGENT_COUNT, |
| &slurmdbd_queue_size) != SLURM_SUCCESS) |
| slurmdbd_queue_size = 0; |
| |
| buffer = init_buf(BUF_SIZE); |
| if (protocol_version >= SLURM_25_11_PROTOCOL_VERSION) { |
| pack_time(now, buffer); |
| debug3("%s: time = %lu", __func__, last_proc_req_start); |
| pack_time(last_proc_req_start, buffer); |
| |
| slurm_mutex_lock(&slurmctld_config.thread_count_lock); |
| debug3("%s: server_thread_count = %u", |
| __func__, slurmctld_config.server_thread_count); |
| pack32(slurmctld_config.server_thread_count, buffer); |
| slurm_mutex_unlock(&slurmctld_config.thread_count_lock); |
| |
| agent_queue_size = retry_list_size(); |
| pack32(agent_queue_size, buffer); |
| agent_count = get_agent_count(); |
| pack32(agent_count, buffer); |
| agent_thread_count = get_agent_thread_count(); |
| pack32(agent_thread_count, buffer); |
| pack32(slurmdbd_queue_size, buffer); |
| pack32(slurmctld_diag_stats.latency, buffer); |
| |
| pack32(slurmctld_diag_stats.jobs_submitted, buffer); |
| pack32(slurmctld_diag_stats.jobs_started, buffer); |
| pack32(slurmctld_diag_stats.jobs_completed, buffer); |
| pack32(slurmctld_diag_stats.jobs_canceled, buffer); |
| pack32(slurmctld_diag_stats.jobs_failed, buffer); |
| |
| pack32(slurmctld_diag_stats.jobs_pending, buffer); |
| pack32(slurmctld_diag_stats.jobs_running, buffer); |
| pack_time(slurmctld_diag_stats.job_states_ts, buffer); |
| |
| pack32(slurmctld_diag_stats.schedule_cycle_max, buffer); |
| pack32(slurmctld_diag_stats.schedule_cycle_last, buffer); |
| pack64(slurmctld_diag_stats.schedule_cycle_sum, buffer); |
| pack32(slurmctld_diag_stats.schedule_cycle_counter, buffer); |
| pack32(slurmctld_diag_stats.schedule_cycle_depth, buffer); |
| pack32_array(slurmctld_diag_stats.schedule_exit, |
| SCHEDULE_EXIT_COUNT, buffer); |
| pack32(slurmctld_diag_stats.schedule_queue_len, buffer); |
| |
| pack32(slurmctld_diag_stats.backfilled_jobs, buffer); |
| pack32(slurmctld_diag_stats.last_backfilled_jobs, buffer); |
| pack32(slurmctld_diag_stats.bf_cycle_counter, buffer); |
| pack64(slurmctld_diag_stats.bf_cycle_sum, buffer); |
| pack32(slurmctld_diag_stats.bf_cycle_last, buffer); |
| pack32(slurmctld_diag_stats.bf_last_depth, buffer); |
| pack32(slurmctld_diag_stats.bf_last_depth_try, buffer); |
| |
| pack32(slurmctld_diag_stats.bf_queue_len, buffer); |
| pack32(slurmctld_diag_stats.bf_cycle_max, buffer); |
| pack_time(slurmctld_diag_stats.bf_when_last_cycle, buffer); |
| pack32(slurmctld_diag_stats.bf_depth_sum, buffer); |
| pack32(slurmctld_diag_stats.bf_depth_try_sum, buffer); |
| pack32(slurmctld_diag_stats.bf_queue_len_sum, buffer); |
| pack32(slurmctld_diag_stats.bf_table_size, buffer); |
| pack32(slurmctld_diag_stats.bf_table_size_sum, buffer); |
| |
| pack32(slurmctld_diag_stats.bf_active, buffer); |
| pack32(slurmctld_diag_stats.backfilled_het_jobs, buffer); |
| pack32_array(slurmctld_diag_stats.bf_exit, BF_EXIT_COUNT, |
| buffer); |
| } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) { |
| pack32(1, buffer); /* please remove on next version */ |
| |
| pack_time(now, buffer); |
| debug3("%s: time = %lu", __func__, last_proc_req_start); |
| pack_time(last_proc_req_start, buffer); |
| |
| slurm_mutex_lock(&slurmctld_config.thread_count_lock); |
| debug3("%s: server_thread_count = %u", |
| __func__, slurmctld_config.server_thread_count); |
| pack32(slurmctld_config.server_thread_count, buffer); |
| slurm_mutex_unlock(&slurmctld_config.thread_count_lock); |
| |
| agent_queue_size = retry_list_size(); |
| pack32(agent_queue_size, buffer); |
| agent_count = get_agent_count(); |
| pack32(agent_count, buffer); |
| agent_thread_count = get_agent_thread_count(); |
| pack32(agent_thread_count, buffer); |
| pack32(slurmdbd_queue_size, buffer); |
| pack32(slurmctld_diag_stats.latency, buffer); |
| |
| pack32(slurmctld_diag_stats.jobs_submitted, buffer); |
| pack32(slurmctld_diag_stats.jobs_started, buffer); |
| pack32(slurmctld_diag_stats.jobs_completed, buffer); |
| pack32(slurmctld_diag_stats.jobs_canceled, buffer); |
| pack32(slurmctld_diag_stats.jobs_failed, buffer); |
| |
| pack32(slurmctld_diag_stats.jobs_pending, buffer); |
| pack32(slurmctld_diag_stats.jobs_running, buffer); |
| pack_time(slurmctld_diag_stats.job_states_ts, buffer); |
| |
| pack32(slurmctld_diag_stats.schedule_cycle_max, buffer); |
| pack32(slurmctld_diag_stats.schedule_cycle_last, buffer); |
| pack32(slurmctld_diag_stats.schedule_cycle_sum, buffer); |
| pack32(slurmctld_diag_stats.schedule_cycle_counter, buffer); |
| pack32(slurmctld_diag_stats.schedule_cycle_depth, buffer); |
| pack32_array(slurmctld_diag_stats.schedule_exit, |
| SCHEDULE_EXIT_COUNT, buffer); |
| pack32(slurmctld_diag_stats.schedule_queue_len, buffer); |
| |
| pack32(slurmctld_diag_stats.backfilled_jobs, buffer); |
| pack32(slurmctld_diag_stats.last_backfilled_jobs, buffer); |
| pack32(slurmctld_diag_stats.bf_cycle_counter, buffer); |
| pack64(slurmctld_diag_stats.bf_cycle_sum, buffer); |
| pack32(slurmctld_diag_stats.bf_cycle_last, buffer); |
| pack32(slurmctld_diag_stats.bf_last_depth, buffer); |
| pack32(slurmctld_diag_stats.bf_last_depth_try, buffer); |
| |
| pack32(slurmctld_diag_stats.bf_queue_len, buffer); |
| pack32(slurmctld_diag_stats.bf_cycle_max, buffer); |
| pack_time(slurmctld_diag_stats.bf_when_last_cycle, buffer); |
| pack32(slurmctld_diag_stats.bf_depth_sum, buffer); |
| pack32(slurmctld_diag_stats.bf_depth_try_sum, buffer); |
| pack32(slurmctld_diag_stats.bf_queue_len_sum, buffer); |
| pack32(slurmctld_diag_stats.bf_table_size, buffer); |
| pack32(slurmctld_diag_stats.bf_table_size_sum, buffer); |
| |
| pack32(slurmctld_diag_stats.bf_active, buffer); |
| pack32(slurmctld_diag_stats.backfilled_het_jobs, buffer); |
| pack32_array(slurmctld_diag_stats.bf_exit, BF_EXIT_COUNT, |
| buffer); |
| } |
| |
| return buffer; |
| } |
| |
| /* Reset all scheduling statistics |
| * level IN - clear backfilled_jobs count if set */ |
| extern void reset_stats(int level) |
| { |
| slurmctld_diag_stats.proc_req_raw = 0; |
| slurmctld_diag_stats.proc_req_threads = 0; |
| slurmctld_diag_stats.schedule_cycle_max = 0; |
| slurmctld_diag_stats.schedule_cycle_sum = 0; |
| slurmctld_diag_stats.schedule_cycle_counter = 0; |
| slurmctld_diag_stats.schedule_cycle_depth = 0; |
| slurmctld_diag_stats.jobs_submitted = 0; |
| slurmctld_diag_stats.jobs_started = 0; |
| slurmctld_diag_stats.jobs_completed = 0; |
| slurmctld_diag_stats.jobs_canceled = 0; |
| slurmctld_diag_stats.jobs_failed = 0; |
| |
| memset(slurmctld_diag_stats.schedule_exit, 0, |
| sizeof(slurmctld_diag_stats.schedule_exit)); |
| |
| /* Just resetting this value when reset requested explicitly */ |
| if (level) |
| slurmctld_diag_stats.backfilled_jobs = 0; |
| |
| slurmctld_diag_stats.last_backfilled_jobs = 0; |
| slurmctld_diag_stats.backfilled_het_jobs = 0; |
| slurmctld_diag_stats.bf_cycle_counter = 0; |
| slurmctld_diag_stats.bf_cycle_sum = 0; |
| slurmctld_diag_stats.bf_cycle_last = 0; |
| slurmctld_diag_stats.bf_depth_sum = 0; |
| slurmctld_diag_stats.bf_depth_try_sum = 0; |
| slurmctld_diag_stats.bf_queue_len = 0; |
| slurmctld_diag_stats.bf_queue_len_sum = 0; |
| slurmctld_diag_stats.bf_table_size_sum = 0; |
| slurmctld_diag_stats.bf_cycle_max = 0; |
| slurmctld_diag_stats.bf_last_depth = 0; |
| slurmctld_diag_stats.bf_last_depth_try = 0; |
| |
| memset(slurmctld_diag_stats.bf_exit, 0, |
| sizeof(slurmctld_diag_stats.bf_exit)); |
| |
| last_proc_req_start = time(NULL); |
| } |
| |
| static void _free_job_stats(job_stats_t *j) |
| { |
| FREE_NULL_BITMAP(j->node_bitmap); |
| xfree(j->user_name); |
| xfree(j->partition); |
| xfree(j->account); |
| xfree(j); |
| } |
| |
| static void _free_part_stats(partition_stats_t *s) |
| { |
| xfree(s->name); |
| xfree(s); |
| } |
| |
| static void _free_ua_stats(ua_stats_t *ua) |
| { |
| statistics_free_jobs(ua->s); |
| xfree(ua->name); |
| xfree(ua); |
| } |
| |
| static int _find_ua_by_name(void *x, void *y) |
| { |
| ua_stats_t *ua = x; |
| |
| if (!xstrcmp(ua->name, (char *) y)) |
| return 1; |
| return 0; |
| } |
| |
| static int _statistics_part_aggregate_job(void *x, void *arg) |
| { |
| partition_stats_t *ps = arg; |
| job_stats_t *j = x; |
| |
| if (xstrcmp(j->partition, ps->name)) |
| return SLURM_SUCCESS; |
| |
| if (IS_JOB_BOOT_FAIL(j)) |
| ps->jobs_bootfail++; |
| if (IS_JOB_CANCELLED(j)) |
| ps->jobs_cancelled++; |
| if (IS_JOB_COMPLETE(j)) |
| ps->jobs_completed++; |
| if (IS_JOB_DEADLINE(j)) |
| ps->jobs_deadline++; |
| if (IS_JOB_FAILED(j)) |
| ps->jobs_failed++; |
| if (IS_JOB_NODE_FAILED(j)) |
| ps->jobs_node_failed++; |
| if (IS_JOB_OOM(j)) |
| ps->jobs_oom++; |
| if (IS_JOB_PENDING(j)) { |
| if ((j->state_reason == WAIT_HELD) || |
| (j->state_reason == WAIT_HELD_USER)) { |
| ps->jobs_hold += j->job_array_cnt; |
| } |
| ps->jobs_pending += j->job_array_cnt; |
| } |
| if (IS_JOB_PREEMPTED(j)) |
| ps->jobs_preempted++; |
| if (IS_JOB_RUNNING(j)) |
| ps->jobs_running++; |
| if (IS_JOB_SUSPENDED(j)) |
| ps->jobs_suspended++; |
| if (IS_JOB_TIMEOUT(j)) |
| ps->jobs_timeout++; |
| |
| /* Derived job states */ |
| if (IS_JOB_COMPLETING(j)) |
| ps->jobs_completing++; |
| if (IS_JOB_CONFIGURING(j)) |
| ps->jobs_configuring++; |
| if (IS_JOB_EXPEDITING(j)) |
| ps->jobs_expediting++; |
| if (IS_JOB_FED_REQUEUED(j)) |
| ps->jobs_fed_requeued++; |
| if (IS_JOB_FINISHED(j)) |
| ps->jobs_finished++; |
| if (IS_JOB_POWER_UP_NODE(j)) |
| ps->jobs_powerup_node++; |
| if (IS_JOB_REQUEUED(j)) |
| ps->jobs_requeued++; |
| if (IS_JOB_RESIZING(j)) |
| ps->jobs_resizing++; |
| if (IS_JOB_REVOKED(j)) |
| ps->jobs_revoked++; |
| if (IS_JOB_SIGNALING(j)) |
| ps->jobs_signaling++; |
| if (IS_JOB_STAGE_OUT(j)) |
| ps->jobs_stageout++; |
| if (IS_JOB_STARTED(j)) |
| ps->jobs_started++; |
| |
| /* Custom metric for Slinky */ |
| if (j->state_reason & WAIT_PART_NODE_LIMIT) |
| ps->jobs_wait_part_node_limit++; |
| |
| if (IS_JOB_RUNNING(j) || IS_JOB_SUSPENDED(j)) { |
| ps->jobs_cpus_alloc += j->cpus_alloc; |
| ps->jobs_gpus_alloc += j->gpus_alloc; |
| ps->jobs_memory_alloc += j->memory_alloc; |
| } |
| |
| if (!IS_JOB_COMPLETED(j)) |
| ps->jobs++; |
| |
| /* |
| * Custom Slinky metrics for autoscaling |
| * Max of the min_nodes required of all pending jobs in that partition |
| * Max of the max_nodes required of all pending jobs in that partition |
| */ |
| if (IS_JOB_PENDING(j)) { |
| if (!((j->state_reason == WAIT_HELD) || |
| (j->state_reason == WAIT_HELD_USER))) { |
| ps->jobs_max_job_nodes_nohold = |
| MAX(ps->jobs_max_job_nodes_nohold, |
| j->max_nodes); |
| ps->jobs_min_job_nodes_nohold = |
| MAX(ps->jobs_min_job_nodes_nohold, |
| j->min_nodes); |
| } |
| ps->jobs_max_job_nodes = |
| MAX(ps->jobs_max_job_nodes, j->max_nodes); |
| ps->jobs_min_job_nodes = |
| MAX(ps->jobs_min_job_nodes, j->min_nodes); |
| } |
| |
| return SLURM_SUCCESS; |
| } |
| |
| static void _statistics_part_aggregate_node(partition_stats_t *ps, |
| node_stats_t *ns) |
| { |
| ps->nodes_cpus_alloc += ns->cpus_alloc; |
| ps->nodes_cpus_efctv += ns->cpus_efctv; |
| ps->nodes_cpus_idle += ns->cpus_idle; |
| |
| ps->total_gpus += ns->gpus_total; |
| |
| ps->nodes_mem_alloc += ns->mem_alloc; |
| ps->nodes_mem_avail += ns->mem_avail; |
| ps->nodes_mem_free += ns->mem_free; |
| ps->nodes_mem_total += ns->mem_total; |
| |
| if (IS_NODE_FUTURE(ns)) |
| ps->nodes_future++; |
| else if (IS_NODE_DOWN(ns)) |
| ps->nodes_down++; |
| else if (IS_NODE_MIXED(ns)) |
| ps->nodes_mixed++; |
| else if (IS_NODE_ALLOCATED(ns)) |
| ps->nodes_alloc++; |
| else if (IS_NODE_IDLE(ns)) |
| ps->nodes_idle++; |
| else if (IS_NODE_UNKNOWN(ns)) |
| ps->nodes_unknown++; |
| |
| /* Derived node states */ |
| if (IS_NODE_COMPLETING(ns)) |
| ps->nodes_cg++; |
| if (IS_NODE_BLOCKED(ns)) |
| ps->nodes_blocked++; |
| if (IS_NODE_CLOUD(ns)) |
| ps->nodes_cloud++; |
| |
| if (IS_NODE_DRAINING(ns)) |
| ps->nodes_draining++; |
| else if (IS_NODE_DRAIN(ns)) |
| ps->nodes_drain++; |
| |
| if (IS_NODE_DRAINED(ns)) |
| ps->nodes_drained++; |
| if (IS_NODE_DYNAMIC_FUTURE(ns)) |
| ps->nodes_dyn_future++; |
| if (IS_NODE_DYNAMIC_NORM(ns)) |
| ps->nodes_dyn_normal++; |
| if (IS_NODE_EXTERNAL(ns)) |
| ps->nodes_external++; |
| if (IS_NODE_FAIL(ns)) |
| ps->nodes_fail++; |
| if (IS_NODE_INVALID_REG(ns)) |
| ps->nodes_invalid_reg++; |
| if (IS_NODE_MAINT(ns)) |
| ps->nodes_maint++; |
| if (IS_NODE_NO_RESPOND(ns)) |
| ps->nodes_no_resp++; |
| if (IS_NODE_PLANNED(ns)) |
| ps->nodes_planned++; |
| if (IS_NODE_POWER_DOWN(ns)) |
| ps->nodes_power_down++; |
| if (IS_NODE_POWER_UP(ns)) |
| ps->nodes_power_up++; |
| if (IS_NODE_POWERED_DOWN(ns)) |
| ps->nodes_powered_down++; |
| if (IS_NODE_POWERING_DOWN(ns)) |
| ps->nodes_powering_down++; |
| if (IS_NODE_POWERING_UP(ns)) |
| ps->nodes_powering_up++; |
| if (IS_NODE_REBOOT_ISSUED(ns)) |
| ps->nodes_reboot_issued++; |
| if (IS_NODE_REBOOT_REQUESTED(ns)) |
| ps->nodes_reboot_requested++; |
| if (IS_NODE_RES(ns)) |
| ps->nodes_resv++; |
| } |
| |
| static int _get_part_statistics(void *x, void *arg) |
| { |
| part_record_t *part_ptr = x; |
| partitions_stats_t *ps = ((foreach_part_gen_stats_t *) arg)->ps; |
| nodes_stats_t *ns = ((foreach_part_gen_stats_t *) arg)->ns; |
| jobs_stats_t *js = ((foreach_part_gen_stats_t *) arg)->js; |
| partition_stats_t *s = xmalloc(sizeof(*s)); |
| |
| s->total_cpus = part_ptr->total_cpus; |
| s->total_nodes = part_ptr->total_nodes; |
| s->name = xstrdup(part_ptr->name); |
| |
| for (int i = 0; i < ns->node_stats_count; i++) { |
| if (!bit_test(part_ptr->node_bitmap, i)) |
| continue; |
| _statistics_part_aggregate_node(s, ns->node_stats_table[i]); |
| } |
| list_for_each_ro(js->jobs, _statistics_part_aggregate_job, s); |
| |
| list_append(ps->parts, s); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| static int _fill_jobs_statistics(void *x, void *arg) |
| { |
| job_record_t *j = x; |
| foreach_fill_jobs_args_t *args = arg; |
| jobs_stats_t *js = args->js; |
| int gpu_tres_pos = args->gpu_tres_pos; |
| job_stats_t *new = xmalloc(sizeof(*new)); |
| |
| new->job_array_cnt = (j->array_recs && j->array_recs->task_cnt) ? |
| j->array_recs->task_cnt : |
| 1; |
| |
| if (IS_JOB_BOOT_FAIL(j)) |
| js->bootfail++; |
| if (IS_JOB_CANCELLED(j)) |
| js->cancelled++; |
| if (IS_JOB_COMPLETE(j)) |
| js->completed++; |
| if (IS_JOB_DEADLINE(j)) |
| js->deadline++; |
| if (IS_JOB_FAILED(j)) |
| js->failed++; |
| if (IS_JOB_NODE_FAILED(j)) |
| js->node_failed++; |
| if (IS_JOB_OOM(j)) |
| js->oom++; |
| if (IS_JOB_PENDING(j)) { |
| if ((j->state_reason == WAIT_HELD) || |
| (j->state_reason == WAIT_HELD_USER)) { |
| js->hold += new->job_array_cnt; |
| } |
| js->pending += new->job_array_cnt; |
| } |
| if (IS_JOB_PREEMPTED(j)) |
| js->preempted++; |
| if (IS_JOB_RUNNING(j)) |
| js->running++; |
| if (IS_JOB_SUSPENDED(j)) |
| js->suspended++; |
| if (IS_JOB_TIMEOUT(j)) |
| js->timeout++; |
| |
| /* Derived job states */ |
| if (IS_JOB_COMPLETING(j)) |
| js->completing++; |
| if (IS_JOB_CONFIGURING(j)) |
| js->configuring++; |
| if (IS_JOB_EXPEDITING(j)) |
| js->expediting++; |
| if (IS_JOB_FED_REQUEUED(j)) |
| js->fed_requeued++; |
| if (IS_JOB_FINISHED(j)) |
| js->finished++; |
| if (IS_JOB_POWER_UP_NODE(j)) |
| js->powerup_node++; |
| if (IS_JOB_REQUEUED(j)) |
| js->requeued++; |
| if (IS_JOB_RESIZING(j)) |
| js->resizing++; |
| if (IS_JOB_REVOKED(j)) |
| js->revoked++; |
| if (IS_JOB_SIGNALING(j)) |
| js->signaling++; |
| if (IS_JOB_STAGE_OUT(j)) |
| js->stageout++; |
| if (IS_JOB_STARTED(j)) |
| js->started++; |
| |
| /* Store individual records */ |
| new->job_id = j->job_id; |
| new->partition = |
| xstrdup(j->part_ptr ? j->part_ptr->name : j->partition); |
| if (!j->user_name) |
| new->user_name = user_from_job(j); |
| else |
| new->user_name = xstrdup(j->user_name); |
| new->account = xstrdup(j->account); |
| |
| if (IS_JOB_RUNNING(j) || IS_JOB_SUSPENDED(j)) { |
| new->cpus_alloc = j->total_cpus; |
| new->memory_alloc = |
| (j->tres_alloc_cnt ? j->tres_alloc_cnt[TRES_ARRAY_MEM] : |
| 0); |
| if (j->node_bitmap) |
| new->node_bitmap = bit_copy(j->node_bitmap); |
| |
| if ((gpu_tres_pos >= 0) && j->tres_alloc_cnt) |
| new->gpus_alloc = j->tres_alloc_cnt[gpu_tres_pos]; |
| |
| js->cpus_alloc += new->cpus_alloc; |
| js->memory_alloc += new->memory_alloc; |
| js->gpus_alloc += new->gpus_alloc; |
| /* |
| * Several jobs can share the same nodes, so update the |
| * node_bitmap of all-jobs-stats adding the bits of this job. |
| */ |
| if (j->node_bitmap) { |
| if (!js->node_bitmap) |
| js->node_bitmap = bit_copy(j->node_bitmap); |
| else |
| bit_or(js->node_bitmap, j->node_bitmap); |
| |
| /* Update the metric with the new count, O(1) cost. */ |
| js->nodes_alloc = bit_set_count(js->node_bitmap); |
| } |
| } |
| |
| /* |
| * Custom Slinky metrics for autoscaling |
| * Max of the min_nodes required of all pending jobs in that partition |
| * Max of the max_nodes required of all pending jobs in that partition |
| */ |
| if (IS_JOB_PENDING(j) && j->details) { |
| new->min_nodes = j->details->min_nodes; |
| new->max_nodes = MAX(j->details->max_nodes, new->min_nodes); |
| } |
| new->job_state = j->job_state; |
| new->state_reason = j->state_reason; |
| list_append(js->jobs, new); |
| |
| js->job_cnt += new->job_array_cnt; |
| |
| return SLURM_SUCCESS; |
| } |
| |
| static void _aggregate_job_to_jobs(jobs_stats_t *s, job_stats_t *j) |
| { |
| if (IS_JOB_BOOT_FAIL(j)) |
| s->bootfail++; |
| if (IS_JOB_CANCELLED(j)) |
| s->cancelled++; |
| if (IS_JOB_COMPLETE(j)) |
| s->completed++; |
| if (IS_JOB_DEADLINE(j)) |
| s->deadline++; |
| if (IS_JOB_FAILED(j)) |
| s->failed++; |
| if (IS_JOB_NODE_FAILED(j)) |
| s->node_failed++; |
| if (IS_JOB_OOM(j)) |
| s->oom++; |
| if (IS_JOB_PENDING(j)) { |
| if ((j->state_reason == WAIT_HELD) || |
| (j->state_reason == WAIT_HELD_USER)) { |
| s->hold += j->job_array_cnt; |
| } |
| s->pending += j->job_array_cnt; |
| } |
| if (IS_JOB_PREEMPTED(j)) |
| s->preempted++; |
| if (IS_JOB_RUNNING(j)) |
| s->running++; |
| if (IS_JOB_SUSPENDED(j)) |
| s->suspended++; |
| if (IS_JOB_TIMEOUT(j)) |
| s->timeout++; |
| |
| /* Derived job states */ |
| if (IS_JOB_COMPLETING(j)) |
| s->completing++; |
| if (IS_JOB_CONFIGURING(j)) |
| s->configuring++; |
| if (IS_JOB_EXPEDITING(j)) |
| s->expediting++; |
| if (IS_JOB_FED_REQUEUED(j)) |
| s->fed_requeued++; |
| if (IS_JOB_FINISHED(j)) |
| s->finished++; |
| if (IS_JOB_POWER_UP_NODE(j)) |
| s->powerup_node++; |
| if (IS_JOB_REQUEUED(j)) |
| s->requeued++; |
| if (IS_JOB_RESIZING(j)) |
| s->resizing++; |
| if (IS_JOB_REVOKED(j)) |
| s->revoked++; |
| if (IS_JOB_SIGNALING(j)) |
| s->signaling++; |
| if (IS_JOB_STAGE_OUT(j)) |
| s->stageout++; |
| if (IS_JOB_STARTED(j)) |
| s->started++; |
| |
| if (IS_JOB_RUNNING(j) || IS_JOB_SUSPENDED(j)) { |
| s->cpus_alloc += j->cpus_alloc; |
| s->gpus_alloc += j->gpus_alloc; |
| s->memory_alloc += j->memory_alloc; |
| /* |
| * Several jobs can share the same nodes, so update the |
| * node_bitmap of all-jobs-stats adding the bits of this job. |
| */ |
| if (j->node_bitmap) { |
| if (!s->node_bitmap) |
| s->node_bitmap = bit_copy(j->node_bitmap); |
| else |
| bit_or(s->node_bitmap, j->node_bitmap); |
| |
| /* Update the metric with the new count, O(1) cost. */ |
| s->nodes_alloc = bit_set_count(s->node_bitmap); |
| } |
| } |
| |
| s->job_cnt++; |
| } |
| |
| static int _get_users_accts(void *x, void *args) |
| { |
| users_accts_stats_t *s = args; |
| job_stats_t *j = x; |
| ua_stats_t *us, *as; |
| |
| us = list_find_first(s->users, _find_ua_by_name, j->user_name); |
| as = list_find_first(s->accounts, _find_ua_by_name, j->account); |
| |
| if (!us) { |
| us = xmalloc(sizeof(*us)); |
| us->s = xmalloc(sizeof(*us->s)); |
| us->name = xstrdup(j->user_name); |
| list_append(s->users, us); |
| } |
| if (!as) { |
| as = xmalloc(sizeof(*as)); |
| as->s = xmalloc(sizeof(*as->s)); |
| as->name = xstrdup(j->account); |
| list_append(s->accounts, as); |
| } |
| |
| _aggregate_job_to_jobs(us->s, j); |
| _aggregate_job_to_jobs(as->s, j); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| extern jobs_stats_t *statistics_get_jobs(bool lock) |
| { |
| slurmctld_lock_t job_read_lock = { |
| .conf = READ_LOCK, |
| .job = READ_LOCK, |
| .part = READ_LOCK, |
| .fed = READ_LOCK, |
| }; |
| jobs_stats_t *s = xmalloc(sizeof(*s)); |
| slurmdb_tres_rec_t tres_rec = { .type = "gres", .name = "gpu" }; |
| |
| /* Avoid looking for tres pos at each iteration, store in args. */ |
| foreach_fill_jobs_args_t args = { |
| .gpu_tres_pos = assoc_mgr_find_tres_pos(&tres_rec, false), |
| .js = s, |
| }; |
| |
| s->jobs = list_create((ListDelF) _free_job_stats); |
| |
| if (lock) |
| lock_slurmctld(job_read_lock); |
| |
| list_for_each_ro(job_list, _fill_jobs_statistics, &args); |
| |
| if (lock) |
| unlock_slurmctld(job_read_lock); |
| |
| return s; |
| } |
| |
| extern nodes_stats_t *statistics_get_nodes(bool lock) |
| { |
| slurmctld_lock_t node_write_lock = { |
| .conf = READ_LOCK, |
| .node = READ_LOCK, |
| .part = READ_LOCK, |
| .select_node = WRITE_LOCK, /*select_g_select_nodeinfo_set_all*/ |
| }; |
| slurmdb_tres_rec_t tres_rec = { .type = "gres", .name = "gpu" }; |
| int gpu_tres_pos = assoc_mgr_find_tres_pos(&tres_rec, false); |
| uint32_t gpu_plugin_id = gres_get_gpu_plugin_id(); |
| node_record_t *node_ptr; |
| nodes_stats_t *s = xmalloc(sizeof(*s)); |
| |
| if (lock) |
| lock_slurmctld(node_write_lock); |
| |
| select_g_select_nodeinfo_set_all(); |
| |
| s->node_stats_table = |
| xcalloc(node_record_count, sizeof(*s->node_stats_table)); |
| s->node_stats_count = node_record_count; |
| |
| for (int i = 0; (node_ptr = next_node(&i)); i++) { |
| uint16_t idle_cpus = 0; |
| node_stats_t *n; |
| node_select_stats_t *select_stats = node_select_stats_array[i]; |
| |
| n = xmalloc(sizeof(*n)); |
| |
| idle_cpus = node_ptr->cpus_efctv - select_stats->alloc_cpus; |
| n->name = xstrdup(node_ptr->name); |
| n->cpus_alloc = select_stats->alloc_cpus; |
| n->cpus_efctv = node_ptr->cpus_efctv; |
| n->cpus_idle = idle_cpus; |
| n->cpus_total = node_ptr->cpus; |
| n->mem_alloc = select_stats->alloc_memory; |
| n->mem_avail = node_ptr->real_memory - node_ptr->mem_spec_limit; |
| n->mem_free = |
| (node_ptr->free_mem == NO_VAL64 ? 0 : |
| node_ptr->free_mem); |
| n->mem_total = node_ptr->real_memory; |
| n->node_state = node_ptr->node_state; |
| |
| /* GPU totals from TRES */ |
| if ((gpu_tres_pos >= 0) && node_ptr->tres_cnt) |
| n->gpus_total = node_ptr->tres_cnt[gpu_tres_pos]; |
| |
| /* GPU allocated from GRES node state */ |
| if (node_ptr->gres_list) { |
| gres_state_t *gres_state_node; |
| gres_state_node = |
| list_find_first(node_ptr->gres_list, |
| gres_find_id, &gpu_plugin_id); |
| if (gres_state_node) { |
| gres_node_state_t *gres_ns = |
| gres_state_node->gres_data; |
| n->gpus_alloc = gres_ns->gres_cnt_alloc; |
| } |
| } |
| |
| s->node_stats_table[i] = n; |
| |
| /* |
| * Base states are unique but can be combined with any other |
| * derived node state |
| */ |
| |
| /* Base states */ |
| if (IS_NODE_FUTURE(node_ptr)) |
| s->future++; |
| else if (IS_NODE_DOWN(node_ptr)) |
| s->down++; |
| else if ((idle_cpus && (idle_cpus < node_ptr->cpus_efctv)) || |
| (select_stats->alloc_tres_fmt_str && |
| (idle_cpus == node_ptr->cpus_efctv))) { |
| n->node_state &= NODE_STATE_FLAGS; |
| n->node_state |= NODE_STATE_MIXED; |
| s->mixed++; |
| /* |
| * The MIXED state is not set by the controller, it must |
| * be inferred - e.g. see also _set_node_mixed() |
| */ |
| } else if (IS_NODE_ALLOCATED(node_ptr)) |
| s->alloc++; |
| else if (IS_NODE_IDLE(node_ptr)) |
| s->idle++; |
| else if (IS_NODE_UNKNOWN(node_ptr)) |
| s->unknown++; |
| |
| /* Derived node states */ |
| if (IS_NODE_COMPLETING(node_ptr)) |
| s->cg++; |
| if (IS_NODE_BLOCKED(node_ptr)) |
| s->blocked++; |
| if (IS_NODE_CLOUD(node_ptr)) |
| s->cloud++; |
| |
| if (IS_NODE_DRAINING(node_ptr)) |
| s->draining++; |
| else if (IS_NODE_DRAIN(node_ptr)) |
| s->drain++; |
| |
| if (IS_NODE_DRAINED(node_ptr)) |
| s->drained++; |
| if (IS_NODE_DYNAMIC_FUTURE(node_ptr)) |
| s->dyn_future++; |
| if (IS_NODE_DYNAMIC_NORM(node_ptr)) |
| s->dyn_normal++; |
| if (IS_NODE_EXTERNAL(node_ptr)) |
| s->external++; |
| if (IS_NODE_FAIL(node_ptr)) |
| s->fail++; |
| if (IS_NODE_INVALID_REG(node_ptr)) |
| s->invalid_reg++; |
| if (IS_NODE_MAINT(node_ptr)) |
| s->maint++; |
| if (IS_NODE_NO_RESPOND(node_ptr)) |
| s->no_resp++; |
| if (IS_NODE_PLANNED(node_ptr)) |
| s->planned++; |
| if (IS_NODE_POWER_DOWN(node_ptr)) |
| s->power_down++; |
| if (IS_NODE_POWER_UP(node_ptr)) |
| s->power_up++; |
| if (IS_NODE_POWERED_DOWN(node_ptr)) |
| s->powered_down++; |
| if (IS_NODE_POWERING_DOWN(node_ptr)) |
| s->powering_down++; |
| if (IS_NODE_POWERING_UP(node_ptr)) |
| s->powering_up++; |
| if (IS_NODE_REBOOT_ISSUED(node_ptr)) |
| s->reboot_issued++; |
| if (IS_NODE_REBOOT_REQUESTED(node_ptr)) |
| s->reboot_requested++; |
| if (IS_NODE_RES(node_ptr)) |
| s->resv++; |
| } |
| |
| if (lock) |
| unlock_slurmctld(node_write_lock); |
| |
| return s; |
| } |
| |
| extern partitions_stats_t *statistics_get_parts(nodes_stats_t *ns, |
| jobs_stats_t *js, bool lock) |
| { |
| slurmctld_lock_t part_read_lock = { |
| .conf = READ_LOCK, |
| .part = READ_LOCK, |
| }; |
| foreach_part_gen_stats_t p = { 0 }; |
| partitions_stats_t *ps = xmalloc(sizeof(*ps)); |
| |
| p.ps = ps; |
| p.ns = ns; |
| p.js = js; |
| |
| ps->parts = list_create((ListDelF) _free_part_stats); |
| |
| if (lock) |
| lock_slurmctld(part_read_lock); |
| |
| list_for_each_ro(part_list, _get_part_statistics, &p); |
| |
| if (lock) |
| unlock_slurmctld(part_read_lock); |
| |
| return ps; |
| } |
| |
| extern scheduling_stats_t *statistics_get_sched(void) |
| { |
| scheduling_stats_t *s = xmalloc(sizeof(*s)); |
| |
| s->agent_queue_size = retry_list_size(); |
| s->agent_count = get_agent_count(); |
| s->agent_thread_count = get_agent_thread_count(); |
| s->diag_stats = xmalloc(sizeof(*(s->diag_stats))); |
| memcpy(s->diag_stats, &slurmctld_diag_stats, |
| sizeof(slurmctld_diag_stats)); |
| |
| /* extended diag stats */ |
| if (s->diag_stats->schedule_cycle_counter > 0) { |
| s->sched_mean_cycle = s->diag_stats->schedule_cycle_sum / |
| s->diag_stats->schedule_cycle_counter; |
| s->sched_mean_depth_cycle = |
| s->diag_stats->schedule_cycle_depth / |
| s->diag_stats->schedule_cycle_counter; |
| } |
| if (s->diag_stats->bf_cycle_counter > 0) { |
| s->bf_depth_mean = s->diag_stats->bf_depth_sum / |
| s->diag_stats->bf_cycle_counter; |
| s->bf_try_depth_mean = s->diag_stats->bf_depth_try_sum / |
| s->diag_stats->bf_cycle_counter; |
| s->bf_queue_len_mean = s->diag_stats->bf_queue_len_sum / |
| s->diag_stats->bf_cycle_counter; |
| s->bf_mean_table_sz = s->diag_stats->bf_table_size_sum / |
| s->diag_stats->bf_cycle_counter; |
| s->bf_mean_cycle = s->diag_stats->bf_cycle_sum / |
| s->diag_stats->bf_cycle_counter; |
| } |
| s->last_proc_req_start = last_proc_req_start; |
| if (acct_storage_g_get_data(acct_db_conn, ACCT_STORAGE_INFO_AGENT_COUNT, |
| &s->slurmdbd_queue_size) != SLURM_SUCCESS) |
| s->slurmdbd_queue_size = 0; |
| s->time = time(NULL); |
| slurm_mutex_lock(&slurmctld_config.thread_count_lock); |
| s->server_thread_count = slurmctld_config.server_thread_count; |
| slurm_mutex_unlock(&slurmctld_config.thread_count_lock); |
| |
| return s; |
| } |
| |
| extern users_accts_stats_t *statistics_get_users_accounts(jobs_stats_t *js) |
| { |
| users_accts_stats_t *ua = xmalloc(sizeof(*ua)); |
| |
| ua->users = list_create((ListDelF) _free_ua_stats); |
| ua->accounts = list_create((ListDelF) _free_ua_stats); |
| |
| list_for_each(js->jobs, _get_users_accts, ua); |
| |
| return ua; |
| } |
| |
| extern void statistics_free_jobs(jobs_stats_t *s) |
| { |
| FREE_NULL_BITMAP(s->node_bitmap); |
| FREE_NULL_LIST(s->jobs); |
| xfree(s); |
| } |
| |
| extern void statistics_free_parts(partitions_stats_t *s) |
| { |
| FREE_NULL_LIST(s->parts); |
| xfree(s); |
| } |
| |
| extern void statistics_free_nodes(nodes_stats_t *s) |
| { |
| for (int i = 0; i < s->node_stats_count; i++) { |
| if (!s->node_stats_table[i]) |
| continue; |
| xfree(s->node_stats_table[i]->name); |
| xfree(s->node_stats_table[i]); |
| } |
| xfree(s->node_stats_table); |
| xfree(s); |
| } |
| |
| extern void statistics_free_sched(scheduling_stats_t *s) |
| { |
| xfree(s->diag_stats); |
| xfree(s); |
| } |
| |
| extern void statistics_free_users_accounts(users_accts_stats_t *s) |
| { |
| FREE_NULL_LIST(s->users); |
| FREE_NULL_LIST(s->accounts); |
| xfree(s); |
| } |