Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34c0dad7 authored by Michael Holzheu's avatar Michael Holzheu Committed by Martin Schwidefsky
Browse files

s390/hypfs: Add diagnose 0c support



With this feature, you can read the CPU performance metrics provided by the
z/VM diagnose 0C. This then allows to get the management time for each
online CPU of the guest where the diagnose is executed.

The new debugfs file /sys/kernel/debug/s390_hypfs/diag_0c exports the
diag0C binary data to user space via an open/read/close interface.

The binary data consists out of a header structure followed by an
array that contains the diagnose 0c data for each online CPU.

Signed-off-by: default avatarMichael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 45cce4cc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5,3 +5,4 @@
obj-$(CONFIG_S390_HYPFS_FS) += s390_hypfs.o

s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o hypfs_sprp.o
s390_hypfs-objs += hypfs_diag0c.o
+4 −0
Original line number Diff line number Diff line
@@ -37,6 +37,10 @@ extern int hypfs_vm_init(void);
extern void hypfs_vm_exit(void);
extern int hypfs_vm_create_files(struct dentry *root);

/* VM diagnose 0c */
int hypfs_diag0c_init(void);
void hypfs_diag0c_exit(void);

/* Set Partition-Resource Parameter */
int hypfs_sprp_init(void);
void hypfs_sprp_exit(void);
+139 −0
Original line number Diff line number Diff line
/*
 * Hypervisor filesystem for Linux on s390
 *
 * Diag 0C implementation
 *
 * Copyright IBM Corp. 2014
 */

#include <linux/slab.h>
#include <linux/cpu.h>
#include <asm/hypfs.h>
#include "hypfs.h"

#define DBFS_D0C_HDR_VERSION 0

/*
 * Execute diagnose 0c in 31 bit mode
 */
static void diag0c(struct hypfs_diag0c_entry *entry)
{
	asm volatile (
#ifdef CONFIG_64BIT
		"	sam31\n"
		"	diag	%0,%0,0x0c\n"
		"	sam64\n"
#else
		"	diag %0,%0,0x0c\n"
#endif
		: /* no output register */
		: "a" (entry)
		: "memory");
}

/*
 * Get hypfs_diag0c_entry from CPU vector and store diag0c data
 */
static void diag0c_fn(void *data)
{
	diag0c(((void **) data)[smp_processor_id()]);
}

/*
 * Allocate buffer and store diag 0c data
 */
static void *diag0c_store(unsigned int *count)
{
	struct hypfs_diag0c_data *diag0c_data;
	unsigned int cpu_count, cpu, i;
	void **cpu_vec;

	get_online_cpus();
	cpu_count = num_online_cpus();
	cpu_vec = kmalloc(sizeof(*cpu_vec) * num_possible_cpus(), GFP_KERNEL);
	if (!cpu_vec)
		goto fail_put_online_cpus;
	/* Note: Diag 0c needs 8 byte alignment and real storage */
	diag0c_data = kzalloc(sizeof(struct hypfs_diag0c_hdr) +
			      cpu_count * sizeof(struct hypfs_diag0c_entry),
			      GFP_KERNEL | GFP_DMA);
	if (!diag0c_data)
		goto fail_kfree_cpu_vec;
	i = 0;
	/* Fill CPU vector for each online CPU */
	for_each_online_cpu(cpu) {
		diag0c_data->entry[i].cpu = cpu;
		cpu_vec[cpu] = &diag0c_data->entry[i++];
	}
	/* Collect data all CPUs */
	on_each_cpu(diag0c_fn, cpu_vec, 1);
	*count = cpu_count;
	kfree(cpu_vec);
	put_online_cpus();
	return diag0c_data;

fail_kfree_cpu_vec:
	kfree(cpu_vec);
fail_put_online_cpus:
	put_online_cpus();
	return ERR_PTR(-ENOMEM);
}

/*
 * Hypfs DBFS callback: Free diag 0c data
 */
static void dbfs_diag0c_free(const void *data)
{
	kfree(data);
}

/*
 * Hypfs DBFS callback: Create diag 0c data
 */
static int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size)
{
	struct hypfs_diag0c_data *diag0c_data;
	unsigned int count;

	diag0c_data = diag0c_store(&count);
	if (IS_ERR(diag0c_data))
		return PTR_ERR(diag0c_data);
	memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr));
	get_tod_clock_ext(diag0c_data->hdr.tod_ext);
	diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry);
	diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION;
	diag0c_data->hdr.count = count;
	*data = diag0c_data;
	*data_free_ptr = diag0c_data;
	*size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr);
	return 0;
}

/*
 * Hypfs DBFS file structure
 */
static struct hypfs_dbfs_file dbfs_file_0c = {
	.name		= "diag_0c",
	.data_create	= dbfs_diag0c_create,
	.data_free	= dbfs_diag0c_free,
};

/*
 * Initialize diag 0c interface for z/VM
 */
int __init hypfs_diag0c_init(void)
{
	if (!MACHINE_IS_VM)
		return 0;
	return hypfs_dbfs_create_file(&dbfs_file_0c);
}

/*
 * Shutdown diag 0c interface for z/VM
 */
void hypfs_diag0c_exit(void)
{
	if (!MACHINE_IS_VM)
		return;
	hypfs_dbfs_remove_file(&dbfs_file_0c);
}
+8 −1
Original line number Diff line number Diff line
@@ -482,10 +482,14 @@ static int __init hypfs_init(void)
		rc = -ENODATA;
		goto fail_hypfs_vm_exit;
	}
	if (hypfs_diag0c_init()) {
		rc = -ENODATA;
		goto fail_hypfs_sprp_exit;
	}
	s390_kobj = kobject_create_and_add("s390", hypervisor_kobj);
	if (!s390_kobj) {
		rc = -ENOMEM;
		goto fail_hypfs_sprp_exit;
		goto fail_hypfs_diag0c_exit;
	}
	rc = register_filesystem(&hypfs_type);
	if (rc)
@@ -494,6 +498,8 @@ static int __init hypfs_init(void)

fail_filesystem:
	kobject_put(s390_kobj);
fail_hypfs_diag0c_exit:
	hypfs_diag0c_exit();
fail_hypfs_sprp_exit:
	hypfs_sprp_exit();
fail_hypfs_vm_exit:
@@ -510,6 +516,7 @@ static void __exit hypfs_exit(void)
{
	unregister_filesystem(&hypfs_type);
	kobject_put(s390_kobj);
	hypfs_diag0c_exit();
	hypfs_sprp_exit();
	hypfs_vm_exit();
	hypfs_diag_exit();
+32 −3
Original line number Diff line number Diff line
/*
 * IOCTL interface for hypfs
 * Structures for hypfs interface
 *
 * Copyright IBM Corp. 2013
 *
 * Author: Martin Schwidefsky <schwidefsky@de.ibm.com>
 */

#ifndef _ASM_HYPFS_CTL_H
#define _ASM_HYPFS_CTL_H
#ifndef _ASM_HYPFS_H
#define _ASM_HYPFS_H

#include <linux/types.h>

/*
 * IOCTL for binary interface /sys/kernel/debug/diag_304
 */
struct hypfs_diag304 {
	__u32	args[2];
	__u64	data;
@@ -22,4 +25,30 @@ struct hypfs_diag304 {
#define HYPFS_DIAG304 \
	_IOWR(HYPFS_IOCTL_MAGIC, 0x20, struct hypfs_diag304)

/*
 * Structures for binary interface /sys/kernel/debug/diag_0c
 */
struct hypfs_diag0c_hdr {
	__u64	len;		/* Length of diag0c buffer without header */
	__u16	version;	/* Version of header */
	char	reserved1[6];	/* Reserved */
	char	tod_ext[16];	/* TOD clock for diag0c */
	__u64	count;		/* Number of entries (CPUs) in diag0c array */
	char	reserved2[24];	/* Reserved */
};

struct hypfs_diag0c_entry {
	char	date[8];	/* MM/DD/YY in EBCDIC */
	char	time[8];	/* HH:MM:SS in EBCDIC */
	__u64	virtcpu;	/* Virtual time consumed by the virt CPU (us) */
	__u64	totalproc;	/* Total of virtual and simulation time (us) */
	__u32	cpu;		/* Linux logical CPU number */
	__u32	reserved;	/* Align to 8 byte */
};

struct hypfs_diag0c_data {
	struct hypfs_diag0c_hdr		hdr;		/* 64 byte header */
	struct hypfs_diag0c_entry	entry[];	/* diag0c entry array */
};

#endif