s390/sysinfo,topology: provide raw stsi 15,1,x data via debugfs
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Mon, 13 Mar 2017 14:58:59 +0000 (15:58 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 22 Mar 2017 07:29:15 +0000 (08:29 +0100)
Provide the raw stsi 15,1,x data contents via debugfs. This makes it
much easier to debug unexpected scheduling domains on machines that
provide cpu topology information.

Therefore this file adds a new 's390/stsi' debugfs directory with a
file for each possible topology nesting level that is allowed by the
architecture. The files will be created regardless if the machine
supports all, or any, level.  If a level is not supported, or no data
is available, user space can recognize this with a -EINVAL error code
when trying to read such data.
In addition a 'topology' symlink is created that points to the file
that contains the data that is used to create the scheduling domains.

Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/sysinfo.h
arch/s390/kernel/sysinfo.c
arch/s390/kernel/topology.c

index 27394989073a2852333ea0cafe38affa2587c8d0..73bff45ced55268c7f06e52a98be46f7eaa2ea54 100644 (file)
@@ -142,6 +142,15 @@ struct sysinfo_3_2_2 {
 
 extern int topology_max_mnest;
 
+/*
+ * Returns the maximum nesting level supported by the cpu topology code.
+ * The current maximum level is 4 which is the drawer level.
+ */
+static inline int topology_mnest_limit(void)
+{
+       return min(topology_max_mnest, 4);
+}
+
 #define TOPOLOGY_NR_MAG                6
 
 struct topology_core {
index 12b6b138e35454b9c4dd03684a0b61cd73034874..1d4680e38378857ffd4edb404dde6ff631f45579 100644 (file)
@@ -4,6 +4,7 @@
  *            Martin Schwidefsky <schwidefsky@de.ibm.com>,
  */
 
+#include <linux/debugfs.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/proc_fs.h>
@@ -13,6 +14,7 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <asm/ebcdic.h>
+#include <asm/debug.h>
 #include <asm/sysinfo.h>
 #include <asm/cpcmd.h>
 #include <asm/topology.h>
@@ -485,3 +487,81 @@ void calibrate_delay(void)
               "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ),
               (loops_per_jiffy/(5000/HZ)) % 100);
 }
+
+#ifdef CONFIG_DEBUG_FS
+
+#define STSI_FILE(fc, s1, s2)                                                 \
+static int stsi_open_##fc##_##s1##_##s2(struct inode *inode, struct file *file)\
+{                                                                             \
+       file->private_data = (void *) get_zeroed_page(GFP_KERNEL);             \
+       if (!file->private_data)                                               \
+               return -ENOMEM;                                                \
+       if (stsi(file->private_data, fc, s1, s2)) {                            \
+               free_page((unsigned long)file->private_data);                  \
+               file->private_data = NULL;                                     \
+               return -EACCES;                                                \
+       }                                                                      \
+       return nonseekable_open(inode, file);                                  \
+}                                                                             \
+                                                                              \
+static const struct file_operations stsi_##fc##_##s1##_##s2##_fs_ops = {       \
+       .open           = stsi_open_##fc##_##s1##_##s2,                        \
+       .release        = stsi_release,                                        \
+       .read           = stsi_read,                                           \
+       .llseek         = no_llseek,                                           \
+};
+
+static int stsi_release(struct inode *inode, struct file *file)
+{
+       free_page((unsigned long)file->private_data);
+       return 0;
+}
+
+static ssize_t stsi_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
+{
+       return simple_read_from_buffer(buf, size, ppos, file->private_data, PAGE_SIZE);
+}
+
+STSI_FILE(15, 1, 2);
+STSI_FILE(15, 1, 3);
+STSI_FILE(15, 1, 4);
+STSI_FILE(15, 1, 5);
+STSI_FILE(15, 1, 6);
+
+struct stsi_file {
+       const struct file_operations *fops;
+       char *name;
+};
+
+static struct stsi_file stsi_file[] __initdata = {
+       {.fops = &stsi_15_1_2_fs_ops, .name = "15_1_2"},
+       {.fops = &stsi_15_1_3_fs_ops, .name = "15_1_3"},
+       {.fops = &stsi_15_1_4_fs_ops, .name = "15_1_4"},
+       {.fops = &stsi_15_1_5_fs_ops, .name = "15_1_5"},
+       {.fops = &stsi_15_1_6_fs_ops, .name = "15_1_6"},
+};
+
+static __init int stsi_init_debugfs(void)
+{
+       struct dentry *stsi_root;
+       struct stsi_file *sf;
+       int i;
+
+       stsi_root = debugfs_create_dir("stsi", arch_debugfs_dir);
+       if (IS_ERR_OR_NULL(stsi_root))
+               return 0;
+       for (i = 0; i < ARRAY_SIZE(stsi_file); i++) {
+               sf = &stsi_file[i];
+               debugfs_create_file(sf->name, 0400, stsi_root, NULL, sf->fops);
+       }
+       if (IS_ENABLED(CONFIG_SCHED_TOPOLOGY) && MACHINE_HAS_TOPOLOGY) {
+               char link_to[10];
+
+               sprintf(link_to, "15_1_%d", topology_mnest_limit());
+               debugfs_create_symlink("topology", stsi_root, link_to);
+       }
+       return 0;
+}
+device_initcall(stsi_init_debugfs);
+
+#endif /* CONFIG_DEBUG_FS */
index 0537130fb91541eea566217de3035c0c97a4ea71..bb47c92476f0d852ded0b9b50f9756e46480e9b9 100644 (file)
@@ -246,7 +246,7 @@ static void update_cpu_masks(void)
 
 void store_topology(struct sysinfo_15_1_x *info)
 {
-       stsi(info, 15, 1, min(topology_max_mnest, 4));
+       stsi(info, 15, 1, topology_mnest_limit());
 }
 
 static int __arch_update_cpu_topology(void)