aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/hypfs/hypfs_diag.c
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@de.ibm.com>2006-06-23 05:05:06 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-23 10:43:02 -0400
commit24bbb1faf3f0420eb252dd0fdc1e477b1d4d73bd (patch)
treece7f9358fdeaa4f299cb996e8a1d4224d51ee4c4 /arch/s390/hypfs/hypfs_diag.c
parentb9e122c80cd2e10fe18678c63db4717871ed31cf (diff)
[PATCH] s390_hypfs filesystem
On zSeries machines there exists an interface which allows the operating system to retrieve LPAR hypervisor accounting data. For example, it is possible to get usage data for physical and virtual cpus. In order to provide this information to user space programs, I implemented a new virtual Linux file system named 's390_hypfs' using the Linux 2.6 libfs framework. The name 's390_hypfs' stands for 'S390 Hypervisor Filesystem'. All the accounting information is put into different virtual files which can be accessed from user space. All data is represented as ASCII strings. When the file system is mounted the accounting information is retrieved and a file system tree is created with the attribute files containing the cpu information. The content of the files remains unchanged until a new update is made. An update can be triggered from user space through writing 'something' into a special purpose update file. We create the following directory structure: <mount-point>/ update cpus/ <cpu-id> type mgmtime <cpu-id> ... hyp/ type systems/ <lpar-name> cpus/ <cpu-id> type mgmtime cputime onlinetime <cpu-id> ... <lpar-name> cpus/ ... - update: File to trigger update - cpus/: Directory for all physical cpus - cpus/<cpu-id>/: Directory for one physical cpu. - cpus/<cpu-id>/type: Type name of physical zSeries cpu. - cpus/<cpu-id>/mgmtime: Physical-LPAR-management time in microseconds. - hyp/: Directory for hypervisor information - hyp/type: Typ of hypervisor (currently only 'LPAR Hypervisor') - systems/: Directory for all LPARs - systems/<lpar-name>/: Directory for one LPAR. - systems/<lpar-name>/cpus/<cpu-id>/: Directory for the virtual cpus - systems/<lpar-name>/cpus/<cpu-id>/type: Typ of cpu. - systems/<lpar-name>/cpus/<cpu-id>/mgmtime: Accumulated number of microseconds during which a physical CPU was assigned to the logical cpu and the cpu time was consumed by the hypervisor and was not provided to the LPAR (LPAR overhead). - systems/<lpar-name>/cpus/<cpu-id>/cputime: Accumulated number of microseconds during which a physical CPU was assigned to the logical cpu and the cpu time was consumed by the LPAR. - systems/<lpar-name>/cpus/<cpu-id>/onlinetime: Accumulated number of microseconds during which the logical CPU has been online. As mount point for the filesystem /sys/hypervisor/s390 is created. The update process is triggered when writing 'something' into the 'update' file at the top level hypfs directory. You can do this e.g. with 'echo 1 > update'. During the update the whole directory structure is deleted and built up again. Cc: Pekka Enberg <penberg@cs.helsinki.fi> Cc: Ingo Oeser <ioe-lkml@rameria.de> Cc: Joern Engel <joern@wohnheim.fh-wedel.de> Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Michael Holzheu <holzheu@de.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/s390/hypfs/hypfs_diag.c')
-rw-r--r--arch/s390/hypfs/hypfs_diag.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/arch/s390/hypfs/hypfs_diag.c b/arch/s390/hypfs/hypfs_diag.c
new file mode 100644
index 000000000000..efa74af7f04a
--- /dev/null
+++ b/arch/s390/hypfs/hypfs_diag.c
@@ -0,0 +1,696 @@
1/*
2 * fs/hypfs/hypfs_diag.c
3 * Hypervisor filesystem for Linux on s390. Diag 204 and 224
4 * implementation.
5 *
6 * Copyright (C) IBM Corp. 2006
7 * Author(s): Michael Holzheu <holzheu@de.ibm.com>
8 */
9
10#include <linux/types.h>
11#include <linux/errno.h>
12#include <linux/string.h>
13#include <linux/vmalloc.h>
14#include <asm/ebcdic.h>
15#include "hypfs.h"
16
17#define LPAR_NAME_LEN 8 /* lpar name len in diag 204 data */
18#define CPU_NAME_LEN 16 /* type name len of cpus in diag224 name table */
19#define TMP_SIZE 64 /* size of temporary buffers */
20
21/* diag 204 subcodes */
22enum diag204_sc {
23 SUBC_STIB4 = 4,
24 SUBC_RSI = 5,
25 SUBC_STIB6 = 6,
26 SUBC_STIB7 = 7
27};
28
29/* The two available diag 204 data formats */
30enum diag204_format {
31 INFO_SIMPLE = 0,
32 INFO_EXT = 0x00010000
33};
34
35/* bit is set in flags, when physical cpu info is included in diag 204 data */
36#define LPAR_PHYS_FLG 0x80
37
38static char *diag224_cpu_names; /* diag 224 name table */
39static enum diag204_sc diag204_store_sc; /* used subcode for store */
40static enum diag204_format diag204_info_type; /* used diag 204 data format */
41
42static void *diag204_buf; /* 4K aligned buffer for diag204 data */
43static void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */
44static int diag204_buf_pages; /* number of pages for diag204 data */
45
46/*
47 * DIAG 204 data structures and member access functions.
48 *
49 * Since we have two different diag 204 data formats for old and new s390
50 * machines, we do not access the structs directly, but use getter functions for
51 * each struct member instead. This should make the code more readable.
52 */
53
54/* Time information block */
55
56struct info_blk_hdr {
57 __u8 npar;
58 __u8 flags;
59 __u16 tslice;
60 __u16 phys_cpus;
61 __u16 this_part;
62 __u64 curtod;
63} __attribute__ ((packed));
64
65struct x_info_blk_hdr {
66 __u8 npar;
67 __u8 flags;
68 __u16 tslice;
69 __u16 phys_cpus;
70 __u16 this_part;
71 __u64 curtod1;
72 __u64 curtod2;
73 char reserved[40];
74} __attribute__ ((packed));
75
76static inline int info_blk_hdr__size(enum diag204_format type)
77{
78 if (type == INFO_SIMPLE)
79 return sizeof(struct info_blk_hdr);
80 else /* INFO_EXT */
81 return sizeof(struct x_info_blk_hdr);
82}
83
84static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
85{
86 if (type == INFO_SIMPLE)
87 return ((struct info_blk_hdr *)hdr)->npar;
88 else /* INFO_EXT */
89 return ((struct x_info_blk_hdr *)hdr)->npar;
90}
91
92static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
93{
94 if (type == INFO_SIMPLE)
95 return ((struct info_blk_hdr *)hdr)->flags;
96 else /* INFO_EXT */
97 return ((struct x_info_blk_hdr *)hdr)->flags;
98}
99
100static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr)
101{
102 if (type == INFO_SIMPLE)
103 return ((struct info_blk_hdr *)hdr)->phys_cpus;
104 else /* INFO_EXT */
105 return ((struct x_info_blk_hdr *)hdr)->phys_cpus;
106}
107
108/* Partition header */
109
110struct part_hdr {
111 __u8 pn;
112 __u8 cpus;
113 char reserved[6];
114 char part_name[LPAR_NAME_LEN];
115} __attribute__ ((packed));
116
117struct x_part_hdr {
118 __u8 pn;
119 __u8 cpus;
120 __u8 rcpus;
121 __u8 pflag;
122 __u32 mlu;
123 char part_name[LPAR_NAME_LEN];
124 char lpc_name[8];
125 char os_name[8];
126 __u64 online_cs;
127 __u64 online_es;
128 __u8 upid;
129 char reserved1[3];
130 __u32 group_mlu;
131 char group_name[8];
132 char reserved2[32];
133} __attribute__ ((packed));
134
135static inline int part_hdr__size(enum diag204_format type)
136{
137 if (type == INFO_SIMPLE)
138 return sizeof(struct part_hdr);
139 else /* INFO_EXT */
140 return sizeof(struct x_part_hdr);
141}
142
143static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
144{
145 if (type == INFO_SIMPLE)
146 return ((struct part_hdr *)hdr)->cpus;
147 else /* INFO_EXT */
148 return ((struct x_part_hdr *)hdr)->rcpus;
149}
150
151static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
152 char *name)
153{
154 if (type == INFO_SIMPLE)
155 memcpy(name, ((struct part_hdr *)hdr)->part_name,
156 LPAR_NAME_LEN);
157 else /* INFO_EXT */
158 memcpy(name, ((struct x_part_hdr *)hdr)->part_name,
159 LPAR_NAME_LEN);
160 EBCASC(name, LPAR_NAME_LEN);
161 name[LPAR_NAME_LEN] = 0;
162 strstrip(name);
163}
164
165struct cpu_info {
166 __u16 cpu_addr;
167 char reserved1[2];
168 __u8 ctidx;
169 __u8 cflag;
170 __u16 weight;
171 __u64 acc_time;
172 __u64 lp_time;
173} __attribute__ ((packed));
174
175struct x_cpu_info {
176 __u16 cpu_addr;
177 char reserved1[2];
178 __u8 ctidx;
179 __u8 cflag;
180 __u16 weight;
181 __u64 acc_time;
182 __u64 lp_time;
183 __u16 min_weight;
184 __u16 cur_weight;
185 __u16 max_weight;
186 char reseved2[2];
187 __u64 online_time;
188 __u64 wait_time;
189 __u32 pma_weight;
190 __u32 polar_weight;
191 char reserved3[40];
192} __attribute__ ((packed));
193
194/* CPU info block */
195
196static inline int cpu_info__size(enum diag204_format type)
197{
198 if (type == INFO_SIMPLE)
199 return sizeof(struct cpu_info);
200 else /* INFO_EXT */
201 return sizeof(struct x_cpu_info);
202}
203
204static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
205{
206 if (type == INFO_SIMPLE)
207 return ((struct cpu_info *)hdr)->ctidx;
208 else /* INFO_EXT */
209 return ((struct x_cpu_info *)hdr)->ctidx;
210}
211
212static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
213{
214 if (type == INFO_SIMPLE)
215 return ((struct cpu_info *)hdr)->cpu_addr;
216 else /* INFO_EXT */
217 return ((struct x_cpu_info *)hdr)->cpu_addr;
218}
219
220static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
221{
222 if (type == INFO_SIMPLE)
223 return ((struct cpu_info *)hdr)->acc_time;
224 else /* INFO_EXT */
225 return ((struct x_cpu_info *)hdr)->acc_time;
226}
227
228static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
229{
230 if (type == INFO_SIMPLE)
231 return ((struct cpu_info *)hdr)->lp_time;
232 else /* INFO_EXT */
233 return ((struct x_cpu_info *)hdr)->lp_time;
234}
235
236static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
237{
238 if (type == INFO_SIMPLE)
239 return 0; /* online_time not available in simple info */
240 else /* INFO_EXT */
241 return ((struct x_cpu_info *)hdr)->online_time;
242}
243
244/* Physical header */
245
246struct phys_hdr {
247 char reserved1[1];
248 __u8 cpus;
249 char reserved2[6];
250 char mgm_name[8];
251} __attribute__ ((packed));
252
253struct x_phys_hdr {
254 char reserved1[1];
255 __u8 cpus;
256 char reserved2[6];
257 char mgm_name[8];
258 char reserved3[80];
259} __attribute__ ((packed));
260
261static inline int phys_hdr__size(enum diag204_format type)
262{
263 if (type == INFO_SIMPLE)
264 return sizeof(struct phys_hdr);
265 else /* INFO_EXT */
266 return sizeof(struct x_phys_hdr);
267}
268
269static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
270{
271 if (type == INFO_SIMPLE)
272 return ((struct phys_hdr *)hdr)->cpus;
273 else /* INFO_EXT */
274 return ((struct x_phys_hdr *)hdr)->cpus;
275}
276
277/* Physical CPU info block */
278
279struct phys_cpu {
280 __u16 cpu_addr;
281 char reserved1[2];
282 __u8 ctidx;
283 char reserved2[3];
284 __u64 mgm_time;
285 char reserved3[8];
286} __attribute__ ((packed));
287
288struct x_phys_cpu {
289 __u16 cpu_addr;
290 char reserved1[2];
291 __u8 ctidx;
292 char reserved2[3];
293 __u64 mgm_time;
294 char reserved3[80];
295} __attribute__ ((packed));
296
297static inline int phys_cpu__size(enum diag204_format type)
298{
299 if (type == INFO_SIMPLE)
300 return sizeof(struct phys_cpu);
301 else /* INFO_EXT */
302 return sizeof(struct x_phys_cpu);
303}
304
305static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
306{
307 if (type == INFO_SIMPLE)
308 return ((struct phys_cpu *)hdr)->cpu_addr;
309 else /* INFO_EXT */
310 return ((struct x_phys_cpu *)hdr)->cpu_addr;
311}
312
313static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
314{
315 if (type == INFO_SIMPLE)
316 return ((struct phys_cpu *)hdr)->mgm_time;
317 else /* INFO_EXT */
318 return ((struct x_phys_cpu *)hdr)->mgm_time;
319}
320
321static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
322{
323 if (type == INFO_SIMPLE)
324 return ((struct phys_cpu *)hdr)->ctidx;
325 else /* INFO_EXT */
326 return ((struct x_phys_cpu *)hdr)->ctidx;
327}
328
329/* Diagnose 204 functions */
330
331static int diag204(unsigned long subcode, unsigned long size, void *addr)
332{
333 register unsigned long _subcode asm("0") = subcode;
334 register unsigned long _size asm("1") = size;
335
336 asm volatile (" diag %2,%0,0x204\n"
337 "0: \n" ".section __ex_table,\"a\"\n"
338#ifndef __s390x__
339 " .align 4\n"
340 " .long 0b,0b\n"
341#else
342 " .align 8\n"
343 " .quad 0b,0b\n"
344#endif
345 ".previous":"+d" (_subcode), "+d"(_size)
346 :"d"(addr)
347 :"memory");
348 if (_subcode)
349 return -1;
350 else
351 return _size;
352}
353
354/*
355 * For the old diag subcode 4 with simple data format we have to use real
356 * memory. If we use subcode 6 or 7 with extended data format, we can (and
357 * should) use vmalloc, since we need a lot of memory in that case. Currently
358 * up to 93 pages!
359 */
360
361static void diag204_free_buffer(void)
362{
363 if (!diag204_buf)
364 return;
365 if (diag204_buf_vmalloc) {
366 vfree(diag204_buf_vmalloc);
367 diag204_buf_vmalloc = NULL;
368 } else {
369 free_pages((unsigned long) diag204_buf, 0);
370 }
371 diag204_buf_pages = 0;
372 diag204_buf = NULL;
373}
374
375static void *diag204_alloc_vbuf(int pages)
376{
377 /* The buffer has to be page aligned! */
378 diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1));
379 if (!diag204_buf_vmalloc)
380 return ERR_PTR(-ENOMEM);
381 diag204_buf = (void*)((unsigned long)diag204_buf_vmalloc
382 & ~0xfffUL) + 0x1000;
383 diag204_buf_pages = pages;
384 return diag204_buf;
385}
386
387static void *diag204_alloc_rbuf(void)
388{
389 diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0);
390 if (diag204_buf)
391 return ERR_PTR(-ENOMEM);
392 diag204_buf_pages = 1;
393 return diag204_buf;
394}
395
396static void *diag204_get_buffer(enum diag204_format fmt, int *pages)
397{
398 if (diag204_buf) {
399 *pages = diag204_buf_pages;
400 return diag204_buf;
401 }
402 if (fmt == INFO_SIMPLE) {
403 *pages = 1;
404 return diag204_alloc_rbuf();
405 } else {/* INFO_EXT */
406 *pages = diag204(SUBC_RSI | INFO_EXT, 0, 0);
407 if (*pages <= 0)
408 return ERR_PTR(-ENOSYS);
409 else
410 return diag204_alloc_vbuf(*pages);
411 }
412}
413
414/*
415 * diag204_probe() has to find out, which type of diagnose 204 implementation
416 * we have on our machine. Currently there are three possible scanarios:
417 * - subcode 4 + simple data format (only one page)
418 * - subcode 4-6 + extended data format
419 * - subcode 4-7 + extended data format
420 *
421 * Subcode 5 is used to retrieve the size of the data, provided by subcodes
422 * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition
423 * to subcode 6 it provides also information about secondary cpus.
424 * In order to get as much information as possible, we first try
425 * subcode 7, then 6 and if both fail, we use subcode 4.
426 */
427
428static int diag204_probe(void)
429{
430 void *buf;
431 int pages, rc;
432
433 buf = diag204_get_buffer(INFO_EXT, &pages);
434 if (!IS_ERR(buf)) {
435 if (diag204(SUBC_STIB7 | INFO_EXT, pages, buf) >= 0) {
436 diag204_store_sc = SUBC_STIB7;
437 diag204_info_type = INFO_EXT;
438 goto out;
439 }
440 if (diag204(SUBC_STIB6 | INFO_EXT, pages, buf) >= 0) {
441 diag204_store_sc = SUBC_STIB7;
442 diag204_info_type = INFO_EXT;
443 goto out;
444 }
445 diag204_free_buffer();
446 }
447
448 /* subcodes 6 and 7 failed, now try subcode 4 */
449
450 buf = diag204_get_buffer(INFO_SIMPLE, &pages);
451 if (IS_ERR(buf)) {
452 rc = PTR_ERR(buf);
453 goto fail_alloc;
454 }
455 if (diag204(SUBC_STIB4 | INFO_SIMPLE, pages, buf) >= 0) {
456 diag204_store_sc = SUBC_STIB4;
457 diag204_info_type = INFO_SIMPLE;
458 goto out;
459 } else {
460 rc = -ENOSYS;
461 goto fail_store;
462 }
463out:
464 rc = 0;
465fail_store:
466 diag204_free_buffer();
467fail_alloc:
468 return rc;
469}
470
471static void *diag204_store(void)
472{
473 void *buf;
474 int pages;
475
476 buf = diag204_get_buffer(diag204_info_type, &pages);
477 if (IS_ERR(buf))
478 goto out;
479 if (diag204(diag204_store_sc | diag204_info_type, pages, buf) < 0)
480 return ERR_PTR(-ENOSYS);
481out:
482 return buf;
483}
484
485/* Diagnose 224 functions */
486
487static void diag224(void *ptr)
488{
489 asm volatile(" diag %0,%1,0x224\n"
490 : :"d" (0), "d"(ptr) : "memory");
491}
492
493static int diag224_get_name_table(void)
494{
495 /* memory must be below 2GB */
496 diag224_cpu_names = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
497 if (!diag224_cpu_names)
498 return -ENOMEM;
499 diag224(diag224_cpu_names);
500 EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16);
501 return 0;
502}
503
504static void diag224_delete_name_table(void)
505{
506 kfree(diag224_cpu_names);
507}
508
509static int diag224_idx2name(int index, char *name)
510{
511 memcpy(name, diag224_cpu_names + ((index + 1) * CPU_NAME_LEN),
512 CPU_NAME_LEN);
513 name[CPU_NAME_LEN] = 0;
514 strstrip(name);
515 return 0;
516}
517
518__init int hypfs_diag_init(void)
519{
520 int rc;
521
522 if (diag204_probe()) {
523 printk(KERN_ERR "hypfs: diag 204 not working.");
524 return -ENODATA;
525 }
526 rc = diag224_get_name_table();
527 if (rc) {
528 diag224_delete_name_table();
529 printk(KERN_ERR "hypfs: could not get name table.\n");
530 }
531 return rc;
532}
533
534__exit void hypfs_diag_exit(void)
535{
536 diag224_delete_name_table();
537 diag204_free_buffer();
538}
539
540/*
541 * Functions to create the directory structure
542 * *******************************************
543 */
544
545static int hypfs_create_cpu_files(struct super_block *sb,
546 struct dentry *cpus_dir, void *cpu_info)
547{
548 struct dentry *cpu_dir;
549 char buffer[TMP_SIZE];
550 void *rc;
551
552 snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type,
553 cpu_info));
554 cpu_dir = hypfs_mkdir(sb, cpus_dir, buffer);
555 rc = hypfs_create_u64(sb, cpu_dir, "mgmtime",
556 cpu_info__acc_time(diag204_info_type, cpu_info) -
557 cpu_info__lp_time(diag204_info_type, cpu_info));
558 if (IS_ERR(rc))
559 return PTR_ERR(rc);
560 rc = hypfs_create_u64(sb, cpu_dir, "cputime",
561 cpu_info__lp_time(diag204_info_type, cpu_info));
562 if (IS_ERR(rc))
563 return PTR_ERR(rc);
564 if (diag204_info_type == INFO_EXT) {
565 rc = hypfs_create_u64(sb, cpu_dir, "onlinetime",
566 cpu_info__online_time(diag204_info_type,
567 cpu_info));
568 if (IS_ERR(rc))
569 return PTR_ERR(rc);
570 }
571 diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer);
572 rc = hypfs_create_str(sb, cpu_dir, "type", buffer);
573 if (IS_ERR(rc))
574 return PTR_ERR(rc);
575 return 0;
576}
577
578static void *hypfs_create_lpar_files(struct super_block *sb,
579 struct dentry *systems_dir, void *part_hdr)
580{
581 struct dentry *cpus_dir;
582 struct dentry *lpar_dir;
583 char lpar_name[LPAR_NAME_LEN + 1];
584 void *cpu_info;
585 int i;
586
587 part_hdr__part_name(diag204_info_type, part_hdr, lpar_name);
588 lpar_name[LPAR_NAME_LEN] = 0;
589 lpar_dir = hypfs_mkdir(sb, systems_dir, lpar_name);
590 if (IS_ERR(lpar_dir))
591 return lpar_dir;
592 cpus_dir = hypfs_mkdir(sb, lpar_dir, "cpus");
593 if (IS_ERR(cpus_dir))
594 return cpus_dir;
595 cpu_info = part_hdr + part_hdr__size(diag204_info_type);
596 for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) {
597 int rc;
598 rc = hypfs_create_cpu_files(sb, cpus_dir, cpu_info);
599 if (rc)
600 return ERR_PTR(rc);
601 cpu_info += cpu_info__size(diag204_info_type);
602 }
603 return cpu_info;
604}
605
606static int hypfs_create_phys_cpu_files(struct super_block *sb,
607 struct dentry *cpus_dir, void *cpu_info)
608{
609 struct dentry *cpu_dir;
610 char buffer[TMP_SIZE];
611 void *rc;
612
613 snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type,
614 cpu_info));
615 cpu_dir = hypfs_mkdir(sb, cpus_dir, buffer);
616 if (IS_ERR(cpu_dir))
617 return PTR_ERR(cpu_dir);
618 rc = hypfs_create_u64(sb, cpu_dir, "mgmtime",
619 phys_cpu__mgm_time(diag204_info_type, cpu_info));
620 if (IS_ERR(rc))
621 return PTR_ERR(rc);
622 diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer);
623 rc = hypfs_create_str(sb, cpu_dir, "type", buffer);
624 if (IS_ERR(rc))
625 return PTR_ERR(rc);
626 return 0;
627}
628
629static void *hypfs_create_phys_files(struct super_block *sb,
630 struct dentry *parent_dir, void *phys_hdr)
631{
632 int i;
633 void *cpu_info;
634 struct dentry *cpus_dir;
635
636 cpus_dir = hypfs_mkdir(sb, parent_dir, "cpus");
637 if (IS_ERR(cpus_dir))
638 return cpus_dir;
639 cpu_info = phys_hdr + phys_hdr__size(diag204_info_type);
640 for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) {
641 int rc;
642 rc = hypfs_create_phys_cpu_files(sb, cpus_dir, cpu_info);
643 if (rc)
644 return ERR_PTR(rc);
645 cpu_info += phys_cpu__size(diag204_info_type);
646 }
647 return cpu_info;
648}
649
650int hypfs_diag_create_files(struct super_block *sb, struct dentry *root)
651{
652 struct dentry *systems_dir, *hyp_dir;
653 void *time_hdr, *part_hdr;
654 int i, rc;
655 void *buffer, *ptr;
656
657 buffer = diag204_store();
658 if (IS_ERR(buffer))
659 return PTR_ERR(buffer);
660
661 systems_dir = hypfs_mkdir(sb, root, "systems");
662 if (IS_ERR(systems_dir)) {
663 rc = PTR_ERR(systems_dir);
664 goto err_out;
665 }
666 time_hdr = (struct x_info_blk_hdr *)buffer;
667 part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type);
668 for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) {
669 part_hdr = hypfs_create_lpar_files(sb, systems_dir, part_hdr);
670 if (IS_ERR(part_hdr)) {
671 rc = PTR_ERR(part_hdr);
672 goto err_out;
673 }
674 }
675 if (info_blk_hdr__flags(diag204_info_type, time_hdr) & LPAR_PHYS_FLG) {
676 ptr = hypfs_create_phys_files(sb, root, part_hdr);
677 if (IS_ERR(ptr)) {
678 rc = PTR_ERR(ptr);
679 goto err_out;
680 }
681 }
682 hyp_dir = hypfs_mkdir(sb, root, "hyp");
683 if (IS_ERR(hyp_dir)) {
684 rc = PTR_ERR(hyp_dir);
685 goto err_out;
686 }
687 ptr = hypfs_create_str(sb, hyp_dir, "type", "LPAR Hypervisor");
688 if (IS_ERR(ptr)) {
689 rc = PTR_ERR(ptr);
690 goto err_out;
691 }
692 rc = 0;
693
694err_out:
695 return rc;
696}