diff options
author | Michael Holzheu <holzheu@linux.vnet.ibm.com> | 2015-02-09 08:49:10 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2015-02-10 10:38:58 -0500 |
commit | 34c0dad752294f373a0720840f59e186788ba227 (patch) | |
tree | 4d9da02c1edf8c5b4cf57016164747bfabd6faee | |
parent | 45cce4ccafe3cddc924ef5221d22b9853fc9a13c (diff) |
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: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/hypfs/Makefile | 1 | ||||
-rw-r--r-- | arch/s390/hypfs/hypfs.h | 4 | ||||
-rw-r--r-- | arch/s390/hypfs/hypfs_diag0c.c | 139 | ||||
-rw-r--r-- | arch/s390/hypfs/inode.c | 9 | ||||
-rw-r--r-- | arch/s390/include/uapi/asm/hypfs.h | 35 |
5 files changed, 184 insertions, 4 deletions
diff --git a/arch/s390/hypfs/Makefile b/arch/s390/hypfs/Makefile index 06f8d95a16cd..2ee25ba252d6 100644 --- a/arch/s390/hypfs/Makefile +++ b/arch/s390/hypfs/Makefile | |||
@@ -5,3 +5,4 @@ | |||
5 | obj-$(CONFIG_S390_HYPFS_FS) += s390_hypfs.o | 5 | obj-$(CONFIG_S390_HYPFS_FS) += s390_hypfs.o |
6 | 6 | ||
7 | s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o hypfs_sprp.o | 7 | s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o hypfs_sprp.o |
8 | s390_hypfs-objs += hypfs_diag0c.o | ||
diff --git a/arch/s390/hypfs/hypfs.h b/arch/s390/hypfs/hypfs.h index b34b5ab90a31..36d093fa9fb3 100644 --- a/arch/s390/hypfs/hypfs.h +++ b/arch/s390/hypfs/hypfs.h | |||
@@ -37,6 +37,10 @@ extern int hypfs_vm_init(void); | |||
37 | extern void hypfs_vm_exit(void); | 37 | extern void hypfs_vm_exit(void); |
38 | extern int hypfs_vm_create_files(struct dentry *root); | 38 | extern int hypfs_vm_create_files(struct dentry *root); |
39 | 39 | ||
40 | /* VM diagnose 0c */ | ||
41 | int hypfs_diag0c_init(void); | ||
42 | void hypfs_diag0c_exit(void); | ||
43 | |||
40 | /* Set Partition-Resource Parameter */ | 44 | /* Set Partition-Resource Parameter */ |
41 | int hypfs_sprp_init(void); | 45 | int hypfs_sprp_init(void); |
42 | void hypfs_sprp_exit(void); | 46 | 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 index 000000000000..d4c0d3717543 --- /dev/null +++ b/arch/s390/hypfs/hypfs_diag0c.c | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * Hypervisor filesystem for Linux on s390 | ||
3 | * | ||
4 | * Diag 0C implementation | ||
5 | * | ||
6 | * Copyright IBM Corp. 2014 | ||
7 | */ | ||
8 | |||
9 | #include <linux/slab.h> | ||
10 | #include <linux/cpu.h> | ||
11 | #include <asm/hypfs.h> | ||
12 | #include "hypfs.h" | ||
13 | |||
14 | #define DBFS_D0C_HDR_VERSION 0 | ||
15 | |||
16 | /* | ||
17 | * Execute diagnose 0c in 31 bit mode | ||
18 | */ | ||
19 | static void diag0c(struct hypfs_diag0c_entry *entry) | ||
20 | { | ||
21 | asm volatile ( | ||
22 | #ifdef CONFIG_64BIT | ||
23 | " sam31\n" | ||
24 | " diag %0,%0,0x0c\n" | ||
25 | " sam64\n" | ||
26 | #else | ||
27 | " diag %0,%0,0x0c\n" | ||
28 | #endif | ||
29 | : /* no output register */ | ||
30 | : "a" (entry) | ||
31 | : "memory"); | ||
32 | } | ||
33 | |||
34 | /* | ||
35 | * Get hypfs_diag0c_entry from CPU vector and store diag0c data | ||
36 | */ | ||
37 | static void diag0c_fn(void *data) | ||
38 | { | ||
39 | diag0c(((void **) data)[smp_processor_id()]); | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Allocate buffer and store diag 0c data | ||
44 | */ | ||
45 | static void *diag0c_store(unsigned int *count) | ||
46 | { | ||
47 | struct hypfs_diag0c_data *diag0c_data; | ||
48 | unsigned int cpu_count, cpu, i; | ||
49 | void **cpu_vec; | ||
50 | |||
51 | get_online_cpus(); | ||
52 | cpu_count = num_online_cpus(); | ||
53 | cpu_vec = kmalloc(sizeof(*cpu_vec) * num_possible_cpus(), GFP_KERNEL); | ||
54 | if (!cpu_vec) | ||
55 | goto fail_put_online_cpus; | ||
56 | /* Note: Diag 0c needs 8 byte alignment and real storage */ | ||
57 | diag0c_data = kzalloc(sizeof(struct hypfs_diag0c_hdr) + | ||
58 | cpu_count * sizeof(struct hypfs_diag0c_entry), | ||
59 | GFP_KERNEL | GFP_DMA); | ||
60 | if (!diag0c_data) | ||
61 | goto fail_kfree_cpu_vec; | ||
62 | i = 0; | ||
63 | /* Fill CPU vector for each online CPU */ | ||
64 | for_each_online_cpu(cpu) { | ||
65 | diag0c_data->entry[i].cpu = cpu; | ||
66 | cpu_vec[cpu] = &diag0c_data->entry[i++]; | ||
67 | } | ||
68 | /* Collect data all CPUs */ | ||
69 | on_each_cpu(diag0c_fn, cpu_vec, 1); | ||
70 | *count = cpu_count; | ||
71 | kfree(cpu_vec); | ||
72 | put_online_cpus(); | ||
73 | return diag0c_data; | ||
74 | |||
75 | fail_kfree_cpu_vec: | ||
76 | kfree(cpu_vec); | ||
77 | fail_put_online_cpus: | ||
78 | put_online_cpus(); | ||
79 | return ERR_PTR(-ENOMEM); | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * Hypfs DBFS callback: Free diag 0c data | ||
84 | */ | ||
85 | static void dbfs_diag0c_free(const void *data) | ||
86 | { | ||
87 | kfree(data); | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Hypfs DBFS callback: Create diag 0c data | ||
92 | */ | ||
93 | static int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size) | ||
94 | { | ||
95 | struct hypfs_diag0c_data *diag0c_data; | ||
96 | unsigned int count; | ||
97 | |||
98 | diag0c_data = diag0c_store(&count); | ||
99 | if (IS_ERR(diag0c_data)) | ||
100 | return PTR_ERR(diag0c_data); | ||
101 | memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr)); | ||
102 | get_tod_clock_ext(diag0c_data->hdr.tod_ext); | ||
103 | diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry); | ||
104 | diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION; | ||
105 | diag0c_data->hdr.count = count; | ||
106 | *data = diag0c_data; | ||
107 | *data_free_ptr = diag0c_data; | ||
108 | *size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Hypfs DBFS file structure | ||
114 | */ | ||
115 | static struct hypfs_dbfs_file dbfs_file_0c = { | ||
116 | .name = "diag_0c", | ||
117 | .data_create = dbfs_diag0c_create, | ||
118 | .data_free = dbfs_diag0c_free, | ||
119 | }; | ||
120 | |||
121 | /* | ||
122 | * Initialize diag 0c interface for z/VM | ||
123 | */ | ||
124 | int __init hypfs_diag0c_init(void) | ||
125 | { | ||
126 | if (!MACHINE_IS_VM) | ||
127 | return 0; | ||
128 | return hypfs_dbfs_create_file(&dbfs_file_0c); | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * Shutdown diag 0c interface for z/VM | ||
133 | */ | ||
134 | void hypfs_diag0c_exit(void) | ||
135 | { | ||
136 | if (!MACHINE_IS_VM) | ||
137 | return; | ||
138 | hypfs_dbfs_remove_file(&dbfs_file_0c); | ||
139 | } | ||
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index c952b981e4f2..4c8008dd938e 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c | |||
@@ -482,10 +482,14 @@ static int __init hypfs_init(void) | |||
482 | rc = -ENODATA; | 482 | rc = -ENODATA; |
483 | goto fail_hypfs_vm_exit; | 483 | goto fail_hypfs_vm_exit; |
484 | } | 484 | } |
485 | if (hypfs_diag0c_init()) { | ||
486 | rc = -ENODATA; | ||
487 | goto fail_hypfs_sprp_exit; | ||
488 | } | ||
485 | s390_kobj = kobject_create_and_add("s390", hypervisor_kobj); | 489 | s390_kobj = kobject_create_and_add("s390", hypervisor_kobj); |
486 | if (!s390_kobj) { | 490 | if (!s390_kobj) { |
487 | rc = -ENOMEM; | 491 | rc = -ENOMEM; |
488 | goto fail_hypfs_sprp_exit; | 492 | goto fail_hypfs_diag0c_exit; |
489 | } | 493 | } |
490 | rc = register_filesystem(&hypfs_type); | 494 | rc = register_filesystem(&hypfs_type); |
491 | if (rc) | 495 | if (rc) |
@@ -494,6 +498,8 @@ static int __init hypfs_init(void) | |||
494 | 498 | ||
495 | fail_filesystem: | 499 | fail_filesystem: |
496 | kobject_put(s390_kobj); | 500 | kobject_put(s390_kobj); |
501 | fail_hypfs_diag0c_exit: | ||
502 | hypfs_diag0c_exit(); | ||
497 | fail_hypfs_sprp_exit: | 503 | fail_hypfs_sprp_exit: |
498 | hypfs_sprp_exit(); | 504 | hypfs_sprp_exit(); |
499 | fail_hypfs_vm_exit: | 505 | fail_hypfs_vm_exit: |
@@ -510,6 +516,7 @@ static void __exit hypfs_exit(void) | |||
510 | { | 516 | { |
511 | unregister_filesystem(&hypfs_type); | 517 | unregister_filesystem(&hypfs_type); |
512 | kobject_put(s390_kobj); | 518 | kobject_put(s390_kobj); |
519 | hypfs_diag0c_exit(); | ||
513 | hypfs_sprp_exit(); | 520 | hypfs_sprp_exit(); |
514 | hypfs_vm_exit(); | 521 | hypfs_vm_exit(); |
515 | hypfs_diag_exit(); | 522 | hypfs_diag_exit(); |
diff --git a/arch/s390/include/uapi/asm/hypfs.h b/arch/s390/include/uapi/asm/hypfs.h index 37998b449531..b3fe12d8dd87 100644 --- a/arch/s390/include/uapi/asm/hypfs.h +++ b/arch/s390/include/uapi/asm/hypfs.h | |||
@@ -1,16 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | * IOCTL interface for hypfs | 2 | * Structures for hypfs interface |
3 | * | 3 | * |
4 | * Copyright IBM Corp. 2013 | 4 | * Copyright IBM Corp. 2013 |
5 | * | 5 | * |
6 | * Author: Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Author: Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #ifndef _ASM_HYPFS_CTL_H | 9 | #ifndef _ASM_HYPFS_H |
10 | #define _ASM_HYPFS_CTL_H | 10 | #define _ASM_HYPFS_H |
11 | 11 | ||
12 | #include <linux/types.h> | 12 | #include <linux/types.h> |
13 | 13 | ||
14 | /* | ||
15 | * IOCTL for binary interface /sys/kernel/debug/diag_304 | ||
16 | */ | ||
14 | struct hypfs_diag304 { | 17 | struct hypfs_diag304 { |
15 | __u32 args[2]; | 18 | __u32 args[2]; |
16 | __u64 data; | 19 | __u64 data; |
@@ -22,4 +25,30 @@ struct hypfs_diag304 { | |||
22 | #define HYPFS_DIAG304 \ | 25 | #define HYPFS_DIAG304 \ |
23 | _IOWR(HYPFS_IOCTL_MAGIC, 0x20, struct hypfs_diag304) | 26 | _IOWR(HYPFS_IOCTL_MAGIC, 0x20, struct hypfs_diag304) |
24 | 27 | ||
28 | /* | ||
29 | * Structures for binary interface /sys/kernel/debug/diag_0c | ||
30 | */ | ||
31 | struct hypfs_diag0c_hdr { | ||
32 | __u64 len; /* Length of diag0c buffer without header */ | ||
33 | __u16 version; /* Version of header */ | ||
34 | char reserved1[6]; /* Reserved */ | ||
35 | char tod_ext[16]; /* TOD clock for diag0c */ | ||
36 | __u64 count; /* Number of entries (CPUs) in diag0c array */ | ||
37 | char reserved2[24]; /* Reserved */ | ||
38 | }; | ||
39 | |||
40 | struct hypfs_diag0c_entry { | ||
41 | char date[8]; /* MM/DD/YY in EBCDIC */ | ||
42 | char time[8]; /* HH:MM:SS in EBCDIC */ | ||
43 | __u64 virtcpu; /* Virtual time consumed by the virt CPU (us) */ | ||
44 | __u64 totalproc; /* Total of virtual and simulation time (us) */ | ||
45 | __u32 cpu; /* Linux logical CPU number */ | ||
46 | __u32 reserved; /* Align to 8 byte */ | ||
47 | }; | ||
48 | |||
49 | struct hypfs_diag0c_data { | ||
50 | struct hypfs_diag0c_hdr hdr; /* 64 byte header */ | ||
51 | struct hypfs_diag0c_entry entry[]; /* diag0c entry array */ | ||
52 | }; | ||
53 | |||
25 | #endif | 54 | #endif |