Files
s390-tools/hyptop/dg_debugfs_lpar.c
Mete Durlu 0d6e63abdc hyptop: Calculate sample time delta for physical partition
Physical machine information does not include any monotonically
increasing time field like other per CPU information blocks. This
Prevents the percentage calculation for the physical information fields
as the divisor(time_delta) is missing;

field% = (value_current - value_previous) / time_delta

To circumvent that, use the current partition's per CPU online time
values to calculate the time_delta. Integrate time_delta as a new
field(phys_delta_us) specific to the physical systems.

Explicitly CPU0 online time is used, since CPU0 is always online
and cannot be deconfigured on s390x. Its online-time deltas would
match those of the physical CPUs.

Since a new field has to be used for physical systems a new
column in the table also has to be created but instead map the
physical system field to the corresponding regular system fields
for a nicer table view.

Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-12-10 14:40:51 +01:00

422 lines
8.5 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 struct sd_sys *l_cur_lpar;
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[4];
u16 this_part;
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;
}
/*
* Calculate the time delta between samples using current partition's
* online time.
*/
static u64 l_sd_cur_lpar_delta_time_get(void)
{
struct sd_cpu *cpu;
cpu = sd_cpu_get(l_cur_lpar, "0");
if (!cpu->d_prev || !cpu->d_cur)
return 0;
return l_sub_64(cpu->d_cur->online_time_us,
cpu->d_prev->online_time_us);
}
/*
* 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_sys_hdr *sys_hdr, *this_part;
struct l_x_info_blk_hdr *time_hdr;
struct l_debugfs_d204_hdr *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);
this_part = ((void *)time_hdr) + time_hdr->this_part;
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);
if (sys_hdr == this_part)
l_cur_lpar = lpar;
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);
sd_phys_delta_time_us_set(sys, l_sd_cur_lpar_delta_time_get());
}
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 physical information items
*/
static struct sd_sys_item *l_phys_item_vec[] = {
&sd_sys_item_core_cnt,
&sd_sys_item_phys_mgm_diff,
&sd_sys_item_mgm,
NULL,
};
/*
* 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,
.phys_item_vec = l_phys_item_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, 1);
return 0;
}