]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
s390/hypfs: Add diagnose 0c support
authorMichael Holzheu <holzheu@linux.vnet.ibm.com>
Mon, 9 Feb 2015 13:49:10 +0000 (14:49 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Tue, 10 Feb 2015 15:38:58 +0000 (16:38 +0100)
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: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/hypfs/Makefile
arch/s390/hypfs/hypfs.h
arch/s390/hypfs/hypfs_diag0c.c [new file with mode: 0644]
arch/s390/hypfs/inode.c
arch/s390/include/uapi/asm/hypfs.h

index 06f8d95a16cdc9767d4538ea1798c32bfac73187..2ee25ba252d68ab98efdcd7a525ae0b29778950d 100644 (file)
@@ -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
index b34b5ab90a313b77bd0561503d882af311a50c7d..36d093fa9fb30b4fd3615130028b16af6e5920af 100644 (file)
@@ -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);
diff --git a/arch/s390/hypfs/hypfs_diag0c.c b/arch/s390/hypfs/hypfs_diag0c.c
new file mode 100644 (file)
index 0000000..d4c0d37
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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);
+}
index c952b981e4f28885223c7f77b51ada2299f03c6a..4c8008dd938e8d0eb5c4fdc18b35b2cfc90f6807 100644 (file)
@@ -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();
index 37998b449531eda3d0a1b415428e7db3d81ab1ba..b3fe12d8dd87f7e642cfd7a3a4836f583996bed7 100644 (file)
@@ -1,16 +1,19 @@
 /*
- * 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