mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
By using core utilization, thread utilization, and management utilization, it is possible to determine how much capacity is left or how much the real CPU SMT utilization is on lpars. Extending hyptop with this new field provides useful information. For more info about real CPU SMT utilization: https://linux.mainframe.blog/smt_utilization/ Briefly: ur = real SMT util uc = core util ut = thread util um = *management util s = **speedup factor ur = ((uc * per_core_thr_count) - ut) / s + (ut - uc) + um * management utilization: logical core time spent on hypervisor instead of logical partition. ** speedup factor: metric used to calculate the SMT utilization on that logical core. This value varies depending on the workload and the machine generation due to hardware optimization level. Signed-off-by: Mete Durlu <meted@linux.ibm.com> Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
389 lines
7.8 KiB
C
389 lines
7.8 KiB
C
/*
|
|
* hyptop - Show hypervisor performance data on System z
|
|
*
|
|
* Hyptop LPAR data gatherer that operates on debugfs
|
|
*
|
|
* Copyright IBM Corp. 2010, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <iconv.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "dg_debugfs.h"
|
|
#include "helper.h"
|
|
#include "hyptop.h"
|
|
#include "sd.h"
|
|
|
|
#define MTID_MASK 0x1f
|
|
#define LPAR_NAME_LEN 8
|
|
#define TMP_SIZE 64
|
|
#define LPAR_PHYS_FLG 0x80
|
|
#define CPU_TYPE_LEN 16
|
|
#define DEBUGFS_FILE "diag_204"
|
|
|
|
static u64 l_update_time_us;
|
|
static long l_204_buf_size;
|
|
|
|
/*
|
|
* Diag data structure definition
|
|
*/
|
|
|
|
struct l_x_info_blk_hdr {
|
|
u8 npar;
|
|
u8 flags;
|
|
u8 reserved1[6];
|
|
u64 curtod1;
|
|
u64 curtod2;
|
|
u8 reserved[40];
|
|
} __attribute__ ((packed));
|
|
|
|
struct l_x_sys_hdr {
|
|
u8 reserved1;
|
|
u8 cpus;
|
|
u8 rcpus;
|
|
u8 reserved2[5];
|
|
char sys_name[LPAR_NAME_LEN];
|
|
u8 reserved3[33];
|
|
u8 mtid;
|
|
u8 reserved4[46];
|
|
} __attribute__ ((packed));
|
|
|
|
static inline void l_sys_hdr__sys_name(struct l_x_sys_hdr *hdr, char *name)
|
|
{
|
|
ht_ebcdic_to_ascii(hdr->sys_name, name, LPAR_NAME_LEN);
|
|
name[LPAR_NAME_LEN] = 0;
|
|
ht_strstrip(name);
|
|
}
|
|
|
|
struct l_x_cpu_info {
|
|
u16 cpu_addr;
|
|
u8 reserved1[2];
|
|
u8 ctidx;
|
|
u8 reserved2[3];
|
|
u64 acc_time;
|
|
u64 lp_time;
|
|
u8 reserved3[6];
|
|
u8 reserved4[2];
|
|
u64 online_time;
|
|
u8 reserved5[24];
|
|
u64 mt_idle_time;
|
|
u8 reserved6[24];
|
|
} __attribute__ ((packed));
|
|
|
|
static int l_thread_cnt(struct l_x_sys_hdr *hdr)
|
|
{
|
|
return (hdr->mtid & MTID_MASK) + 1;
|
|
}
|
|
|
|
static void l_idx2name(int index, char *name)
|
|
{
|
|
switch (index) {
|
|
case 0:
|
|
strcpy(name, SD_CPU_TYPE_STR_CP);
|
|
break;
|
|
case 3:
|
|
strcpy(name, SD_CPU_TYPE_STR_IFL);
|
|
break;
|
|
default:
|
|
strcpy(name, SD_CPU_TYPE_STR_UN);
|
|
}
|
|
}
|
|
|
|
struct l_x_phys_hdr {
|
|
u8 reserved1[1];
|
|
u8 cpus;
|
|
u8 reserved2[94];
|
|
} __attribute__ ((packed));
|
|
|
|
struct l_x_phys_cpu {
|
|
u16 cpu_addr;
|
|
u8 reserved1[2];
|
|
u8 ctidx;
|
|
u8 reserved2[3];
|
|
u64 mgm_time;
|
|
u8 reserved3[80];
|
|
} __attribute__ ((packed));
|
|
|
|
/*
|
|
* Fill CPU with data
|
|
*/
|
|
static void l_sd_cpu_fill(struct sd_cpu *cpu, struct l_x_cpu_info *cpu_info,
|
|
int threads)
|
|
{
|
|
sd_cpu_cpu_time_us_set(cpu, cpu_info->lp_time);
|
|
sd_cpu_threads_per_core_set(cpu, threads);
|
|
if (threads > 1)
|
|
sd_cpu_thread_time_us_set(cpu,
|
|
cpu_info->lp_time * threads - cpu_info->mt_idle_time);
|
|
else
|
|
sd_cpu_thread_time_us_set(cpu, cpu_info->lp_time);
|
|
sd_cpu_mgm_time_us_set(cpu, G0(cpu_info->acc_time - cpu_info->lp_time));
|
|
sd_cpu_online_time_us_set(cpu, cpu_info->online_time);
|
|
sd_cpu_state_set(cpu, SD_CPU_STATE_UNKNOWN);
|
|
}
|
|
|
|
/*
|
|
* Fill system with data
|
|
*/
|
|
static void *l_sd_sys_fill(struct sd_sys *lpar, struct l_x_sys_hdr *sys_hdr)
|
|
{
|
|
struct l_x_cpu_info *cpu_info;
|
|
int i;
|
|
|
|
cpu_info = (struct l_x_cpu_info *) (sys_hdr + 1);
|
|
|
|
for (i = 0; i < sys_hdr->rcpus; i++) {
|
|
char cpu_type[CPU_TYPE_LEN + 1];
|
|
struct sd_cpu *cpu;
|
|
char cpu_id[10];
|
|
|
|
sprintf(cpu_id, "%i", cpu_info->cpu_addr);
|
|
|
|
cpu = sd_cpu_get(lpar, cpu_id);
|
|
if (!cpu) {
|
|
l_idx2name(cpu_info->ctidx, cpu_type);
|
|
cpu = sd_cpu_new(lpar, cpu_id, cpu_type, 1);
|
|
}
|
|
|
|
l_sd_cpu_fill(cpu, cpu_info, lpar->threads_per_core);
|
|
|
|
sd_cpu_commit(cpu);
|
|
cpu_info++;
|
|
}
|
|
return cpu_info;
|
|
}
|
|
|
|
/*
|
|
* Fill one physical CPU with data
|
|
*/
|
|
static void l_sd_cpu_phys_fill(struct sd_sys *sys,
|
|
struct l_x_phys_cpu *cpu_info)
|
|
{
|
|
char cpu_type[CPU_TYPE_LEN + 1];
|
|
char cpu_id[TMP_SIZE];
|
|
struct sd_cpu *cpu;
|
|
|
|
snprintf(cpu_id, TMP_SIZE, "%i", cpu_info->cpu_addr);
|
|
cpu = sd_cpu_get(sys, cpu_id);
|
|
if (!cpu) {
|
|
l_idx2name(cpu_info->ctidx, cpu_type);
|
|
cpu = sd_cpu_new(sys, cpu_id, cpu_type, 1);
|
|
sd_cpu_state_set(cpu, SD_CPU_STATE_UNKNOWN);
|
|
}
|
|
sd_cpu_mgm_time_us_set(cpu, cpu_info->mgm_time);
|
|
sd_cpu_commit(cpu);
|
|
}
|
|
|
|
/*
|
|
* Fill all physical CPUs with data
|
|
*/
|
|
static void l_sd_sys_root_cpu_phys_fill(struct sd_sys *sys,
|
|
struct l_x_phys_hdr *phys_hdr)
|
|
{
|
|
struct l_x_phys_cpu *cpu_info;
|
|
int i;
|
|
|
|
cpu_info = (struct l_x_phys_cpu *) (phys_hdr + 1);
|
|
for (i = 0; i < phys_hdr->cpus; i++) {
|
|
l_sd_cpu_phys_fill(sys, cpu_info);
|
|
cpu_info++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Header for debugfs file "diag_204"
|
|
*/
|
|
struct l_debugfs_d204_hdr {
|
|
u64 len;
|
|
u16 version;
|
|
u8 reserved[54];
|
|
} __attribute__ ((packed));
|
|
|
|
struct l_debugfs_d204 {
|
|
struct l_debugfs_d204_hdr h;
|
|
char buf[];
|
|
} __attribute__ ((packed));
|
|
|
|
/*
|
|
* Read debugfs file
|
|
*/
|
|
static void l_read_debugfs(struct l_debugfs_d204_hdr **hdr,
|
|
struct l_x_info_blk_hdr **data)
|
|
{
|
|
long real_buf_size;
|
|
ssize_t rc;
|
|
void *buf;
|
|
int fh;
|
|
|
|
do {
|
|
fh = dg_debugfs_open(DEBUGFS_FILE);
|
|
*hdr = buf = ht_alloc(l_204_buf_size);
|
|
rc = read(fh, buf, l_204_buf_size);
|
|
if (rc == -1)
|
|
ERR_EXIT_ERRNO("Reading hypervisor data failed");
|
|
close(fh);
|
|
real_buf_size = (*hdr)->len + sizeof(struct l_debugfs_d204_hdr);
|
|
if (rc == real_buf_size)
|
|
break;
|
|
l_204_buf_size = real_buf_size;
|
|
ht_free(buf);
|
|
} while (1);
|
|
*data = buf + sizeof(struct l_debugfs_d204_hdr);
|
|
}
|
|
|
|
/*
|
|
* Fill System Data
|
|
*/
|
|
static void l_sd_sys_root_fill(struct sd_sys *sys)
|
|
{
|
|
struct l_x_info_blk_hdr *time_hdr;
|
|
struct l_debugfs_d204_hdr *hdr;
|
|
struct l_x_sys_hdr *sys_hdr;
|
|
struct sd_sys *lpar;
|
|
char lpar_id[10];
|
|
int i;
|
|
|
|
do {
|
|
l_read_debugfs(&hdr, &time_hdr);
|
|
if (l_update_time_us != ht_ext_tod_2_us(&time_hdr->curtod1)) {
|
|
l_update_time_us = ht_ext_tod_2_us(&time_hdr->curtod1);
|
|
break;
|
|
}
|
|
/*
|
|
* Got old snapshot from kernel. Wait some time until
|
|
* new snapshot is available.
|
|
*/
|
|
ht_free(hdr);
|
|
usleep(DBFS_WAIT_TIME_US);
|
|
} while (1);
|
|
sys_hdr = ((void *) time_hdr) + sizeof(struct l_x_info_blk_hdr);
|
|
for (i = 0; i < time_hdr->npar; i++) {
|
|
l_sys_hdr__sys_name(sys_hdr, lpar_id);
|
|
lpar = sd_sys_get(sys, lpar_id);
|
|
if (!lpar)
|
|
lpar = sd_sys_new(sys, lpar_id);
|
|
lpar->threads_per_core = l_thread_cnt(sys_hdr);
|
|
sys_hdr = l_sd_sys_fill(lpar, sys_hdr);
|
|
sd_sys_commit(lpar);
|
|
}
|
|
|
|
if (time_hdr->flags & LPAR_PHYS_FLG)
|
|
l_sd_sys_root_cpu_phys_fill(sys, (void *) sys_hdr);
|
|
ht_free(hdr);
|
|
sd_sys_commit(sys);
|
|
}
|
|
|
|
/*
|
|
* Update system data
|
|
*/
|
|
static void l_sd_update(void)
|
|
{
|
|
struct sd_sys *root = sd_sys_root_get();
|
|
|
|
sd_sys_update_start(root);
|
|
l_sd_sys_root_fill(root);
|
|
sd_sys_update_end(root, l_update_time_us);
|
|
}
|
|
|
|
/*
|
|
* Supported system items
|
|
*/
|
|
static struct sd_sys_item *l_sys_item_vec[] = {
|
|
&sd_sys_item_core_cnt,
|
|
&sd_sys_item_thread_cnt,
|
|
&sd_sys_item_core_diff,
|
|
&sd_sys_item_thread_diff,
|
|
&sd_sys_item_smt_diff,
|
|
&sd_sys_item_mgm_diff,
|
|
&sd_sys_item_core,
|
|
&sd_sys_item_thread,
|
|
&sd_sys_item_mgm,
|
|
&sd_sys_item_online,
|
|
NULL,
|
|
};
|
|
|
|
/*
|
|
* Default system items
|
|
*/
|
|
static struct sd_sys_item *l_sys_item_enable_vec[] = {
|
|
&sd_sys_item_core_cnt,
|
|
&sd_sys_item_core_diff,
|
|
&sd_sys_item_thread_diff,
|
|
&sd_sys_item_mgm_diff,
|
|
&sd_sys_item_core,
|
|
&sd_sys_item_mgm,
|
|
&sd_sys_item_online,
|
|
NULL,
|
|
};
|
|
|
|
/*
|
|
* Supported CPU items
|
|
*/
|
|
static struct sd_cpu_item *l_cpu_item_vec[] = {
|
|
&sd_cpu_item_type,
|
|
&sd_cpu_item_core_diff,
|
|
&sd_cpu_item_thread_diff,
|
|
&sd_cpu_item_smt_diff,
|
|
&sd_cpu_item_mgm_diff,
|
|
&sd_cpu_item_core,
|
|
&sd_cpu_item_thread,
|
|
&sd_cpu_item_mgm,
|
|
&sd_cpu_item_online,
|
|
NULL,
|
|
};
|
|
|
|
/*
|
|
* Default CPU items
|
|
*/
|
|
static struct sd_cpu_item *l_cpu_item_enable_vec[] = {
|
|
&sd_cpu_item_type,
|
|
&sd_cpu_item_core_diff,
|
|
&sd_cpu_item_thread_diff,
|
|
&sd_cpu_item_mgm_diff,
|
|
NULL,
|
|
};
|
|
|
|
/*
|
|
* Supported CPU types
|
|
*/
|
|
static struct sd_cpu_type *l_cpu_type_vec[] = {
|
|
&sd_cpu_type_ifl,
|
|
&sd_cpu_type_cp,
|
|
&sd_cpu_type_un,
|
|
NULL,
|
|
};
|
|
|
|
/*
|
|
* Define data gatherer structure
|
|
*/
|
|
static struct sd_dg l_sd_dg = {
|
|
.update_sys = l_sd_update,
|
|
.cpu_type_vec = l_cpu_type_vec,
|
|
.sys_item_vec = l_sys_item_vec,
|
|
.sys_item_enable_vec = l_sys_item_enable_vec,
|
|
.cpu_item_vec = l_cpu_item_vec,
|
|
.cpu_item_enable_vec = l_cpu_item_enable_vec,
|
|
};
|
|
|
|
/*
|
|
* Initialize LPAR debugfs data gatherer
|
|
*/
|
|
int dg_debugfs_lpar_init(void)
|
|
{
|
|
int fh;
|
|
|
|
l_204_buf_size = sizeof(struct l_debugfs_d204_hdr);
|
|
fh = dg_debugfs_open(DEBUGFS_FILE);
|
|
if (fh < 0)
|
|
return fh;
|
|
else
|
|
close(fh);
|
|
sd_dg_register(&l_sd_dg, 1);
|
|
return 0;
|
|
}
|