aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@linux.vnet.ibm.com>2010-05-17 04:00:20 -0400
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2010-05-17 04:00:17 -0400
commit57b28f66316d287b9dbf7b28358ca90257230769 (patch)
tree9486fff9e7c746721dbcbbbeb34ddd1307460e89
parentcc961d400e06e78c7aa39aeab1f001eb8f76ef90 (diff)
[S390] s390_hypfs: Add new attributes
In order to access the data of the hypfs diagnose calls from user space also in binary form, this patch adds two new attributes in debugfs: * z/VM: s390_hypfs/d2fc_bin * LPAR: s390_hypfs/d204_bin 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/hypfs.h4
-rw-r--r--arch/s390/hypfs/hypfs_diag.c123
-rw-r--r--arch/s390/hypfs/hypfs_vm.c87
-rw-r--r--arch/s390/hypfs/inode.c40
-rw-r--r--arch/s390/include/asm/timex.h8
5 files changed, 230 insertions, 32 deletions
diff --git a/arch/s390/hypfs/hypfs.h b/arch/s390/hypfs/hypfs.h
index aea572009d60..fa487d4cc08b 100644
--- a/arch/s390/hypfs/hypfs.h
+++ b/arch/s390/hypfs/hypfs.h
@@ -11,6 +11,7 @@
11 11
12#include <linux/fs.h> 12#include <linux/fs.h>
13#include <linux/types.h> 13#include <linux/types.h>
14#include <linux/debugfs.h>
14 15
15#define REG_FILE_MODE 0440 16#define REG_FILE_MODE 0440
16#define UPDATE_FILE_MODE 0220 17#define UPDATE_FILE_MODE 0220
@@ -34,6 +35,9 @@ extern int hypfs_diag_create_files(struct super_block *sb, struct dentry *root);
34 35
35/* VM Hypervisor */ 36/* VM Hypervisor */
36extern int hypfs_vm_init(void); 37extern int hypfs_vm_init(void);
38extern void hypfs_vm_exit(void);
37extern int hypfs_vm_create_files(struct super_block *sb, struct dentry *root); 39extern int hypfs_vm_create_files(struct super_block *sb, struct dentry *root);
38 40
41/* Directory for debugfs files */
42extern struct dentry *hypfs_dbfs_dir;
39#endif /* _HYPFS_H_ */ 43#endif /* _HYPFS_H_ */
diff --git a/arch/s390/hypfs/hypfs_diag.c b/arch/s390/hypfs/hypfs_diag.c
index 5b1acdba6495..1211bb1d2f24 100644
--- a/arch/s390/hypfs/hypfs_diag.c
+++ b/arch/s390/hypfs/hypfs_diag.c
@@ -15,6 +15,7 @@
15#include <linux/slab.h> 15#include <linux/slab.h>
16#include <linux/string.h> 16#include <linux/string.h>
17#include <linux/vmalloc.h> 17#include <linux/vmalloc.h>
18#include <linux/mm.h>
18#include <asm/ebcdic.h> 19#include <asm/ebcdic.h>
19#include "hypfs.h" 20#include "hypfs.h"
20 21
@@ -22,6 +23,8 @@
22#define CPU_NAME_LEN 16 /* type name len of cpus in diag224 name table */ 23#define CPU_NAME_LEN 16 /* type name len of cpus in diag224 name table */
23#define TMP_SIZE 64 /* size of temporary buffers */ 24#define TMP_SIZE 64 /* size of temporary buffers */
24 25
26#define DBFS_D204_HDR_VERSION 0
27
25/* diag 204 subcodes */ 28/* diag 204 subcodes */
26enum diag204_sc { 29enum diag204_sc {
27 SUBC_STIB4 = 4, 30 SUBC_STIB4 = 4,
@@ -47,6 +50,8 @@ static void *diag204_buf; /* 4K aligned buffer for diag204 data */
47static void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */ 50static void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */
48static int diag204_buf_pages; /* number of pages for diag204 data */ 51static int diag204_buf_pages; /* number of pages for diag204 data */
49 52
53static struct dentry *dbfs_d204_file;
54
50/* 55/*
51 * DIAG 204 data structures and member access functions. 56 * DIAG 204 data structures and member access functions.
52 * 57 *
@@ -364,18 +369,21 @@ static void diag204_free_buffer(void)
364 } else { 369 } else {
365 free_pages((unsigned long) diag204_buf, 0); 370 free_pages((unsigned long) diag204_buf, 0);
366 } 371 }
367 diag204_buf_pages = 0;
368 diag204_buf = NULL; 372 diag204_buf = NULL;
369} 373}
370 374
375static void *page_align_ptr(void *ptr)
376{
377 return (void *) PAGE_ALIGN((unsigned long) ptr);
378}
379
371static void *diag204_alloc_vbuf(int pages) 380static void *diag204_alloc_vbuf(int pages)
372{ 381{
373 /* The buffer has to be page aligned! */ 382 /* The buffer has to be page aligned! */
374 diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1)); 383 diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1));
375 if (!diag204_buf_vmalloc) 384 if (!diag204_buf_vmalloc)
376 return ERR_PTR(-ENOMEM); 385 return ERR_PTR(-ENOMEM);
377 diag204_buf = (void*)((unsigned long)diag204_buf_vmalloc 386 diag204_buf = page_align_ptr(diag204_buf_vmalloc);
378 & ~0xfffUL) + 0x1000;
379 diag204_buf_pages = pages; 387 diag204_buf_pages = pages;
380 return diag204_buf; 388 return diag204_buf;
381} 389}
@@ -468,17 +476,26 @@ fail_alloc:
468 return rc; 476 return rc;
469} 477}
470 478
479static int diag204_do_store(void *buf, int pages)
480{
481 int rc;
482
483 rc = diag204((unsigned long) diag204_store_sc |
484 (unsigned long) diag204_info_type, pages, buf);
485 return rc < 0 ? -ENOSYS : 0;
486}
487
471static void *diag204_store(void) 488static void *diag204_store(void)
472{ 489{
473 void *buf; 490 void *buf;
474 int pages; 491 int pages, rc;
475 492
476 buf = diag204_get_buffer(diag204_info_type, &pages); 493 buf = diag204_get_buffer(diag204_info_type, &pages);
477 if (IS_ERR(buf)) 494 if (IS_ERR(buf))
478 goto out; 495 goto out;
479 if (diag204((unsigned long)diag204_store_sc | 496 rc = diag204_do_store(buf, pages);
480 (unsigned long)diag204_info_type, pages, buf) < 0) 497 if (rc)
481 return ERR_PTR(-ENOSYS); 498 return ERR_PTR(rc);
482out: 499out:
483 return buf; 500 return buf;
484} 501}
@@ -526,6 +543,92 @@ static int diag224_idx2name(int index, char *name)
526 return 0; 543 return 0;
527} 544}
528 545
546struct dbfs_d204_hdr {
547 u64 len; /* Length of d204 buffer without header */
548 u16 version; /* Version of header */
549 u8 sc; /* Used subcode */
550 char reserved[53];
551} __attribute__ ((packed));
552
553struct dbfs_d204 {
554 struct dbfs_d204_hdr hdr; /* 64 byte header */
555 char buf[]; /* d204 buffer */
556} __attribute__ ((packed));
557
558struct dbfs_d204_private {
559 struct dbfs_d204 *d204; /* Aligned d204 data with header */
560 void *base; /* Base pointer (needed for vfree) */
561};
562
563static int dbfs_d204_open(struct inode *inode, struct file *file)
564{
565 struct dbfs_d204_private *data;
566 struct dbfs_d204 *d204;
567 int rc, buf_size;
568
569 data = kzalloc(sizeof(*data), GFP_KERNEL);
570 if (!data)
571 return -ENOMEM;
572 buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr);
573 data->base = vmalloc(buf_size);
574 if (!data->base) {
575 rc = -ENOMEM;
576 goto fail_kfree_data;
577 }
578 memset(data->base, 0, buf_size);
579 d204 = page_align_ptr(data->base + sizeof(d204->hdr))
580 - sizeof(d204->hdr);
581 rc = diag204_do_store(&d204->buf, diag204_buf_pages);
582 if (rc)
583 goto fail_vfree_base;
584 d204->hdr.version = DBFS_D204_HDR_VERSION;
585 d204->hdr.len = PAGE_SIZE * diag204_buf_pages;
586 d204->hdr.sc = diag204_store_sc;
587 data->d204 = d204;
588 file->private_data = data;
589 return nonseekable_open(inode, file);
590
591fail_vfree_base:
592 vfree(data->base);
593fail_kfree_data:
594 kfree(data);
595 return rc;
596}
597
598static int dbfs_d204_release(struct inode *inode, struct file *file)
599{
600 struct dbfs_d204_private *data = file->private_data;
601
602 vfree(data->base);
603 kfree(data);
604 return 0;
605}
606
607static ssize_t dbfs_d204_read(struct file *file, char __user *buf,
608 size_t size, loff_t *ppos)
609{
610 struct dbfs_d204_private *data = file->private_data;
611
612 return simple_read_from_buffer(buf, size, ppos, data->d204,
613 data->d204->hdr.len +
614 sizeof(data->d204->hdr));
615}
616
617static const struct file_operations dbfs_d204_ops = {
618 .open = dbfs_d204_open,
619 .read = dbfs_d204_read,
620 .release = dbfs_d204_release,
621};
622
623static int hypfs_dbfs_init(void)
624{
625 dbfs_d204_file = debugfs_create_file("diag_204", 0400, hypfs_dbfs_dir,
626 NULL, &dbfs_d204_ops);
627 if (IS_ERR(dbfs_d204_file))
628 return PTR_ERR(dbfs_d204_file);
629 return 0;
630}
631
529__init int hypfs_diag_init(void) 632__init int hypfs_diag_init(void)
530{ 633{
531 int rc; 634 int rc;
@@ -540,11 +643,17 @@ __init int hypfs_diag_init(void)
540 pr_err("The hardware system does not provide all " 643 pr_err("The hardware system does not provide all "
541 "functions required by hypfs\n"); 644 "functions required by hypfs\n");
542 } 645 }
646 if (diag204_info_type == INFO_EXT) {
647 rc = hypfs_dbfs_init();
648 if (rc)
649 diag204_free_buffer();
650 }
543 return rc; 651 return rc;
544} 652}
545 653
546void hypfs_diag_exit(void) 654void hypfs_diag_exit(void)
547{ 655{
656 debugfs_remove(dbfs_d204_file);
548 diag224_delete_name_table(); 657 diag224_delete_name_table();
549 diag204_free_buffer(); 658 diag204_free_buffer();
550} 659}
diff --git a/arch/s390/hypfs/hypfs_vm.c b/arch/s390/hypfs/hypfs_vm.c
index f0b0d31f0b48..ee5ab1a578e7 100644
--- a/arch/s390/hypfs/hypfs_vm.c
+++ b/arch/s390/hypfs/hypfs_vm.c
@@ -10,14 +10,18 @@
10#include <linux/string.h> 10#include <linux/string.h>
11#include <linux/vmalloc.h> 11#include <linux/vmalloc.h>
12#include <asm/ebcdic.h> 12#include <asm/ebcdic.h>
13#include <asm/timex.h>
13#include "hypfs.h" 14#include "hypfs.h"
14 15
15#define NAME_LEN 8 16#define NAME_LEN 8
17#define DBFS_D2FC_HDR_VERSION 0
16 18
17static char local_guest[] = " "; 19static char local_guest[] = " ";
18static char all_guests[] = "* "; 20static char all_guests[] = "* ";
19static char *guest_query; 21static char *guest_query;
20 22
23static struct dentry *dbfs_d2fc_file;
24
21struct diag2fc_data { 25struct diag2fc_data {
22 __u32 version; 26 __u32 version;
23 __u32 flags; 27 __u32 flags;
@@ -76,23 +80,26 @@ static int diag2fc(int size, char* query, void *addr)
76 return -residual_cnt; 80 return -residual_cnt;
77} 81}
78 82
79static struct diag2fc_data *diag2fc_store(char *query, int *count) 83/*
84 * Allocate buffer for "query" and store diag 2fc at "offset"
85 */
86static void *diag2fc_store(char *query, unsigned int *count, int offset)
80{ 87{
88 void *data;
81 int size; 89 int size;
82 struct diag2fc_data *data;
83 90
84 do { 91 do {
85 size = diag2fc(0, query, NULL); 92 size = diag2fc(0, query, NULL);
86 if (size < 0) 93 if (size < 0)
87 return ERR_PTR(-EACCES); 94 return ERR_PTR(-EACCES);
88 data = vmalloc(size); 95 data = vmalloc(size + offset);
89 if (!data) 96 if (!data)
90 return ERR_PTR(-ENOMEM); 97 return ERR_PTR(-ENOMEM);
91 if (diag2fc(size, query, data) == 0) 98 if (diag2fc(size, query, data + offset) == 0)
92 break; 99 break;
93 vfree(data); 100 vfree(data);
94 } while (1); 101 } while (1);
95 *count = (size / sizeof(*data)); 102 *count = (size / sizeof(struct diag2fc_data));
96 103
97 return data; 104 return data;
98} 105}
@@ -168,9 +175,10 @@ int hypfs_vm_create_files(struct super_block *sb, struct dentry *root)
168{ 175{
169 struct dentry *dir, *file; 176 struct dentry *dir, *file;
170 struct diag2fc_data *data; 177 struct diag2fc_data *data;
171 int rc, i, count = 0; 178 unsigned int count = 0;
179 int rc, i;
172 180
173 data = diag2fc_store(guest_query, &count); 181 data = diag2fc_store(guest_query, &count, 0);
174 if (IS_ERR(data)) 182 if (IS_ERR(data))
175 return PTR_ERR(data); 183 return PTR_ERR(data);
176 184
@@ -218,8 +226,61 @@ failed:
218 return rc; 226 return rc;
219} 227}
220 228
229struct dbfs_d2fc_hdr {
230 u64 len; /* Length of d2fc buffer without header */
231 u16 version; /* Version of header */
232 char tod_ext[16]; /* TOD clock for d2fc */
233 u64 count; /* Number of VM guests in d2fc buffer */
234 char reserved[30];
235} __attribute__ ((packed));
236
237struct dbfs_d2fc {
238 struct dbfs_d2fc_hdr hdr; /* 64 byte header */
239 char buf[]; /* d2fc buffer */
240} __attribute__ ((packed));
241
242static int dbfs_d2fc_open(struct inode *inode, struct file *file)
243{
244 struct dbfs_d2fc *data;
245 unsigned int count;
246
247 data = diag2fc_store(guest_query, &count, sizeof(data->hdr));
248 if (IS_ERR(data))
249 return PTR_ERR(data);
250 get_clock_ext(data->hdr.tod_ext);
251 data->hdr.len = count * sizeof(struct diag2fc_data);
252 data->hdr.version = DBFS_D2FC_HDR_VERSION;
253 data->hdr.count = count;
254 memset(&data->hdr.reserved, 0, sizeof(data->hdr.reserved));
255 file->private_data = data;
256 return nonseekable_open(inode, file);
257}
258
259static int dbfs_d2fc_release(struct inode *inode, struct file *file)
260{
261 diag2fc_free(file->private_data);
262 return 0;
263}
264
265static ssize_t dbfs_d2fc_read(struct file *file, char __user *buf,
266 size_t size, loff_t *ppos)
267{
268 struct dbfs_d2fc *data = file->private_data;
269
270 return simple_read_from_buffer(buf, size, ppos, data, data->hdr.len +
271 sizeof(struct dbfs_d2fc_hdr));
272}
273
274static const struct file_operations dbfs_d2fc_ops = {
275 .open = dbfs_d2fc_open,
276 .read = dbfs_d2fc_read,
277 .release = dbfs_d2fc_release,
278};
279
221int hypfs_vm_init(void) 280int hypfs_vm_init(void)
222{ 281{
282 if (!MACHINE_IS_VM)
283 return 0;
223 if (diag2fc(0, all_guests, NULL) > 0) 284 if (diag2fc(0, all_guests, NULL) > 0)
224 guest_query = all_guests; 285 guest_query = all_guests;
225 else if (diag2fc(0, local_guest, NULL) > 0) 286 else if (diag2fc(0, local_guest, NULL) > 0)
@@ -227,5 +288,17 @@ int hypfs_vm_init(void)
227 else 288 else
228 return -EACCES; 289 return -EACCES;
229 290
291 dbfs_d2fc_file = debugfs_create_file("diag_2fc", 0400, hypfs_dbfs_dir,
292 NULL, &dbfs_d2fc_ops);
293 if (IS_ERR(dbfs_d2fc_file))
294 return PTR_ERR(dbfs_d2fc_file);
295
230 return 0; 296 return 0;
231} 297}
298
299void hypfs_vm_exit(void)
300{
301 if (!MACHINE_IS_VM)
302 return;
303 debugfs_remove(dbfs_d2fc_file);
304}
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index 95c1aaac06cd..6b120f073043 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -46,6 +46,8 @@ static const struct super_operations hypfs_s_ops;
46/* start of list of all dentries, which have to be deleted on update */ 46/* start of list of all dentries, which have to be deleted on update */
47static struct dentry *hypfs_last_dentry; 47static struct dentry *hypfs_last_dentry;
48 48
49struct dentry *hypfs_dbfs_dir;
50
49static void hypfs_update_update(struct super_block *sb) 51static void hypfs_update_update(struct super_block *sb)
50{ 52{
51 struct hypfs_sb_info *sb_info = sb->s_fs_info; 53 struct hypfs_sb_info *sb_info = sb->s_fs_info;
@@ -468,20 +470,22 @@ static int __init hypfs_init(void)
468{ 470{
469 int rc; 471 int rc;
470 472
471 if (MACHINE_IS_VM) { 473 hypfs_dbfs_dir = debugfs_create_dir("s390_hypfs", NULL);
472 if (hypfs_vm_init()) 474 if (IS_ERR(hypfs_dbfs_dir))
473 /* no diag 2fc, just exit */ 475 return PTR_ERR(hypfs_dbfs_dir);
474 return -ENODATA; 476
475 } else { 477 if (hypfs_diag_init()) {
476 if (hypfs_diag_init()) { 478 rc = -ENODATA;
477 rc = -ENODATA; 479 goto fail_debugfs_remove;
478 goto fail_diag; 480 }
479 } 481 if (hypfs_vm_init()) {
482 rc = -ENODATA;
483 goto fail_hypfs_diag_exit;
480 } 484 }
481 s390_kobj = kobject_create_and_add("s390", hypervisor_kobj); 485 s390_kobj = kobject_create_and_add("s390", hypervisor_kobj);
482 if (!s390_kobj) { 486 if (!s390_kobj) {
483 rc = -ENOMEM; 487 rc = -ENOMEM;
484 goto fail_sysfs; 488 goto fail_hypfs_vm_exit;
485 } 489 }
486 rc = register_filesystem(&hypfs_type); 490 rc = register_filesystem(&hypfs_type);
487 if (rc) 491 if (rc)
@@ -490,18 +494,22 @@ static int __init hypfs_init(void)
490 494
491fail_filesystem: 495fail_filesystem:
492 kobject_put(s390_kobj); 496 kobject_put(s390_kobj);
493fail_sysfs: 497fail_hypfs_vm_exit:
494 if (!MACHINE_IS_VM) 498 hypfs_vm_exit();
495 hypfs_diag_exit(); 499fail_hypfs_diag_exit:
496fail_diag: 500 hypfs_diag_exit();
501fail_debugfs_remove:
502 debugfs_remove(hypfs_dbfs_dir);
503
497 pr_err("Initialization of hypfs failed with rc=%i\n", rc); 504 pr_err("Initialization of hypfs failed with rc=%i\n", rc);
498 return rc; 505 return rc;
499} 506}
500 507
501static void __exit hypfs_exit(void) 508static void __exit hypfs_exit(void)
502{ 509{
503 if (!MACHINE_IS_VM) 510 hypfs_diag_exit();
504 hypfs_diag_exit(); 511 hypfs_vm_exit();
512 debugfs_remove(hypfs_dbfs_dir);
505 unregister_filesystem(&hypfs_type); 513 unregister_filesystem(&hypfs_type);
506 kobject_put(s390_kobj); 514 kobject_put(s390_kobj);
507} 515}
diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h
index f174bdaa6b59..09d345a701dc 100644
--- a/arch/s390/include/asm/timex.h
+++ b/arch/s390/include/asm/timex.h
@@ -61,11 +61,15 @@ static inline unsigned long long get_clock (void)
61 return clk; 61 return clk;
62} 62}
63 63
64static inline void get_clock_ext(char *clk)
65{
66 asm volatile("stcke %0" : "=Q" (*clk) : : "cc");
67}
68
64static inline unsigned long long get_clock_xt(void) 69static inline unsigned long long get_clock_xt(void)
65{ 70{
66 unsigned char clk[16]; 71 unsigned char clk[16];
67 72 get_clock_ext(clk);
68 asm volatile("stcke %0" : "=Q" (clk) : : "cc");
69 return *((unsigned long long *)&clk[1]); 73 return *((unsigned long long *)&clk[1]);
70} 74}
71 75