linux/arch/s390/hypfs/hypfs_diag.c
<<
>>
Prefs
   1/*
   2 *  arch/s390/hypfs/hypfs_diag.c
   3 *    Hypervisor filesystem for Linux on s390. Diag 204 and 224
   4 *    implementation.
   5 *
   6 *    Copyright IBM Corp. 2006, 2008
   7 *    Author(s): Michael Holzheu <holzheu@de.ibm.com>
   8 */
   9
  10#define KMSG_COMPONENT "hypfs"
  11#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  12
  13#include <linux/types.h>
  14#include <linux/errno.h>
  15#include <linux/slab.h>
  16#include <linux/string.h>
  17#include <linux/vmalloc.h>
  18#include <linux/mm.h>
  19#include <asm/ebcdic.h>
  20#include "hypfs.h"
  21
  22#define LPAR_NAME_LEN 8         /* lpar name len in diag 204 data */
  23#define CPU_NAME_LEN 16         /* type name len of cpus in diag224 name table */
  24#define TMP_SIZE 64             /* size of temporary buffers */
  25
  26#define DBFS_D204_HDR_VERSION   0
  27
  28/* diag 204 subcodes */
  29enum diag204_sc {
  30        SUBC_STIB4 = 4,
  31        SUBC_RSI = 5,
  32        SUBC_STIB6 = 6,
  33        SUBC_STIB7 = 7
  34};
  35
  36/* The two available diag 204 data formats */
  37enum diag204_format {
  38        INFO_SIMPLE = 0,
  39        INFO_EXT = 0x00010000
  40};
  41
  42/* bit is set in flags, when physical cpu info is included in diag 204 data */
  43#define LPAR_PHYS_FLG  0x80
  44
  45static char *diag224_cpu_names;                 /* diag 224 name table */
  46static enum diag204_sc diag204_store_sc;        /* used subcode for store */
  47static enum diag204_format diag204_info_type;   /* used diag 204 data format */
  48
  49static void *diag204_buf;               /* 4K aligned buffer for diag204 data */
  50static void *diag204_buf_vmalloc;       /* vmalloc pointer for diag204 data */
  51static int diag204_buf_pages;           /* number of pages for diag204 data */
  52
  53static struct dentry *dbfs_d204_file;
  54
  55/*
  56 * DIAG 204 data structures and member access functions.
  57 *
  58 * Since we have two different diag 204 data formats for old and new s390
  59 * machines, we do not access the structs directly, but use getter functions for
  60 * each struct member instead. This should make the code more readable.
  61 */
  62
  63/* Time information block */
  64
  65struct info_blk_hdr {
  66        __u8  npar;
  67        __u8  flags;
  68        __u16 tslice;
  69        __u16 phys_cpus;
  70        __u16 this_part;
  71        __u64 curtod;
  72} __attribute__ ((packed));
  73
  74struct x_info_blk_hdr {
  75        __u8  npar;
  76        __u8  flags;
  77        __u16 tslice;
  78        __u16 phys_cpus;
  79        __u16 this_part;
  80        __u64 curtod1;
  81        __u64 curtod2;
  82        char reserved[40];
  83} __attribute__ ((packed));
  84
  85static inline int info_blk_hdr__size(enum diag204_format type)
  86{
  87        if (type == INFO_SIMPLE)
  88                return sizeof(struct info_blk_hdr);
  89        else /* INFO_EXT */
  90                return sizeof(struct x_info_blk_hdr);
  91}
  92
  93static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
  94{
  95        if (type == INFO_SIMPLE)
  96                return ((struct info_blk_hdr *)hdr)->npar;
  97        else /* INFO_EXT */
  98                return ((struct x_info_blk_hdr *)hdr)->npar;
  99}
 100
 101static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
 102{
 103        if (type == INFO_SIMPLE)
 104                return ((struct info_blk_hdr *)hdr)->flags;
 105        else /* INFO_EXT */
 106                return ((struct x_info_blk_hdr *)hdr)->flags;
 107}
 108
 109static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr)
 110{
 111        if (type == INFO_SIMPLE)
 112                return ((struct info_blk_hdr *)hdr)->phys_cpus;
 113        else /* INFO_EXT */
 114                return ((struct x_info_blk_hdr *)hdr)->phys_cpus;
 115}
 116
 117/* Partition header */
 118
 119struct part_hdr {
 120        __u8 pn;
 121        __u8 cpus;
 122        char reserved[6];
 123        char part_name[LPAR_NAME_LEN];
 124} __attribute__ ((packed));
 125
 126struct x_part_hdr {
 127        __u8  pn;
 128        __u8  cpus;
 129        __u8  rcpus;
 130        __u8  pflag;
 131        __u32 mlu;
 132        char  part_name[LPAR_NAME_LEN];
 133        char  lpc_name[8];
 134        char  os_name[8];
 135        __u64 online_cs;
 136        __u64 online_es;
 137        __u8  upid;
 138        char  reserved1[3];
 139        __u32 group_mlu;
 140        char  group_name[8];
 141        char  reserved2[32];
 142} __attribute__ ((packed));
 143
 144static inline int part_hdr__size(enum diag204_format type)
 145{
 146        if (type == INFO_SIMPLE)
 147                return sizeof(struct part_hdr);
 148        else /* INFO_EXT */
 149                return sizeof(struct x_part_hdr);
 150}
 151
 152static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
 153{
 154        if (type == INFO_SIMPLE)
 155                return ((struct part_hdr *)hdr)->cpus;
 156        else /* INFO_EXT */
 157                return ((struct x_part_hdr *)hdr)->rcpus;
 158}
 159
 160static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
 161                                       char *name)
 162{
 163        if (type == INFO_SIMPLE)
 164                memcpy(name, ((struct part_hdr *)hdr)->part_name,
 165                       LPAR_NAME_LEN);
 166        else /* INFO_EXT */
 167                memcpy(name, ((struct x_part_hdr *)hdr)->part_name,
 168                       LPAR_NAME_LEN);
 169        EBCASC(name, LPAR_NAME_LEN);
 170        name[LPAR_NAME_LEN] = 0;
 171        strim(name);
 172}
 173
 174struct cpu_info {
 175        __u16 cpu_addr;
 176        char  reserved1[2];
 177        __u8  ctidx;
 178        __u8  cflag;
 179        __u16 weight;
 180        __u64 acc_time;
 181        __u64 lp_time;
 182} __attribute__ ((packed));
 183
 184struct x_cpu_info {
 185        __u16 cpu_addr;
 186        char  reserved1[2];
 187        __u8  ctidx;
 188        __u8  cflag;
 189        __u16 weight;
 190        __u64 acc_time;
 191        __u64 lp_time;
 192        __u16 min_weight;
 193        __u16 cur_weight;
 194        __u16 max_weight;
 195        char  reseved2[2];
 196        __u64 online_time;
 197        __u64 wait_time;
 198        __u32 pma_weight;
 199        __u32 polar_weight;
 200        char  reserved3[40];
 201} __attribute__ ((packed));
 202
 203/* CPU info block */
 204
 205static inline int cpu_info__size(enum diag204_format type)
 206{
 207        if (type == INFO_SIMPLE)
 208                return sizeof(struct cpu_info);
 209        else /* INFO_EXT */
 210                return sizeof(struct x_cpu_info);
 211}
 212
 213static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
 214{
 215        if (type == INFO_SIMPLE)
 216                return ((struct cpu_info *)hdr)->ctidx;
 217        else /* INFO_EXT */
 218                return ((struct x_cpu_info *)hdr)->ctidx;
 219}
 220
 221static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
 222{
 223        if (type == INFO_SIMPLE)
 224                return ((struct cpu_info *)hdr)->cpu_addr;
 225        else /* INFO_EXT */
 226                return ((struct x_cpu_info *)hdr)->cpu_addr;
 227}
 228
 229static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
 230{
 231        if (type == INFO_SIMPLE)
 232                return ((struct cpu_info *)hdr)->acc_time;
 233        else /* INFO_EXT */
 234                return ((struct x_cpu_info *)hdr)->acc_time;
 235}
 236
 237static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
 238{
 239        if (type == INFO_SIMPLE)
 240                return ((struct cpu_info *)hdr)->lp_time;
 241        else /* INFO_EXT */
 242                return ((struct x_cpu_info *)hdr)->lp_time;
 243}
 244
 245static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
 246{
 247        if (type == INFO_SIMPLE)
 248                return 0;       /* online_time not available in simple info */
 249        else /* INFO_EXT */
 250                return ((struct x_cpu_info *)hdr)->online_time;
 251}
 252
 253/* Physical header */
 254
 255struct phys_hdr {
 256        char reserved1[1];
 257        __u8 cpus;
 258        char reserved2[6];
 259        char mgm_name[8];
 260} __attribute__ ((packed));
 261
 262struct x_phys_hdr {
 263        char reserved1[1];
 264        __u8 cpus;
 265        char reserved2[6];
 266        char mgm_name[8];
 267        char reserved3[80];
 268} __attribute__ ((packed));
 269
 270static inline int phys_hdr__size(enum diag204_format type)
 271{
 272        if (type == INFO_SIMPLE)
 273                return sizeof(struct phys_hdr);
 274        else /* INFO_EXT */
 275                return sizeof(struct x_phys_hdr);
 276}
 277
 278static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
 279{
 280        if (type == INFO_SIMPLE)
 281                return ((struct phys_hdr *)hdr)->cpus;
 282        else /* INFO_EXT */
 283                return ((struct x_phys_hdr *)hdr)->cpus;
 284}
 285
 286/* Physical CPU info block */
 287
 288struct phys_cpu {
 289        __u16 cpu_addr;
 290        char  reserved1[2];
 291        __u8  ctidx;
 292        char  reserved2[3];
 293        __u64 mgm_time;
 294        char  reserved3[8];
 295} __attribute__ ((packed));
 296
 297struct x_phys_cpu {
 298        __u16 cpu_addr;
 299        char  reserved1[2];
 300        __u8  ctidx;
 301        char  reserved2[3];
 302        __u64 mgm_time;
 303        char  reserved3[80];
 304} __attribute__ ((packed));
 305
 306static inline int phys_cpu__size(enum diag204_format type)
 307{
 308        if (type == INFO_SIMPLE)
 309                return sizeof(struct phys_cpu);
 310        else /* INFO_EXT */
 311                return sizeof(struct x_phys_cpu);
 312}
 313
 314static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
 315{
 316        if (type == INFO_SIMPLE)
 317                return ((struct phys_cpu *)hdr)->cpu_addr;
 318        else /* INFO_EXT */
 319                return ((struct x_phys_cpu *)hdr)->cpu_addr;
 320}
 321
 322static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
 323{
 324        if (type == INFO_SIMPLE)
 325                return ((struct phys_cpu *)hdr)->mgm_time;
 326        else /* INFO_EXT */
 327                return ((struct x_phys_cpu *)hdr)->mgm_time;
 328}
 329
 330static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
 331{
 332        if (type == INFO_SIMPLE)
 333                return ((struct phys_cpu *)hdr)->ctidx;
 334        else /* INFO_EXT */
 335                return ((struct x_phys_cpu *)hdr)->ctidx;
 336}
 337
 338/* Diagnose 204 functions */
 339
 340static int diag204(unsigned long subcode, unsigned long size, void *addr)
 341{
 342        register unsigned long _subcode asm("0") = subcode;
 343        register unsigned long _size asm("1") = size;
 344
 345        asm volatile(
 346                "       diag    %2,%0,0x204\n"
 347                "0:\n"
 348                EX_TABLE(0b,0b)
 349                : "+d" (_subcode), "+d" (_size) : "d" (addr) : "memory");
 350        if (_subcode)
 351                return -1;
 352        return _size;
 353}
 354
 355/*
 356 * For the old diag subcode 4 with simple data format we have to use real
 357 * memory. If we use subcode 6 or 7 with extended data format, we can (and
 358 * should) use vmalloc, since we need a lot of memory in that case. Currently
 359 * up to 93 pages!
 360 */
 361
 362static void diag204_free_buffer(void)
 363{
 364        if (!diag204_buf)
 365                return;
 366        if (diag204_buf_vmalloc) {
 367                vfree(diag204_buf_vmalloc);
 368                diag204_buf_vmalloc = NULL;
 369        } else {
 370                free_pages((unsigned long) diag204_buf, 0);
 371        }
 372        diag204_buf = NULL;
 373}
 374
 375static void *page_align_ptr(void *ptr)
 376{
 377        return (void *) PAGE_ALIGN((unsigned long) ptr);
 378}
 379
 380static void *diag204_alloc_vbuf(int pages)
 381{
 382        /* The buffer has to be page aligned! */
 383        diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1));
 384        if (!diag204_buf_vmalloc)
 385                return ERR_PTR(-ENOMEM);
 386        diag204_buf = page_align_ptr(diag204_buf_vmalloc);
 387        diag204_buf_pages = pages;
 388        return diag204_buf;
 389}
 390
 391static void *diag204_alloc_rbuf(void)
 392{
 393        diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0);
 394        if (!diag204_buf)
 395                return ERR_PTR(-ENOMEM);
 396        diag204_buf_pages = 1;
 397        return diag204_buf;
 398}
 399
 400static void *diag204_get_buffer(enum diag204_format fmt, int *pages)
 401{
 402        if (diag204_buf) {
 403                *pages = diag204_buf_pages;
 404                return diag204_buf;
 405        }
 406        if (fmt == INFO_SIMPLE) {
 407                *pages = 1;
 408                return diag204_alloc_rbuf();
 409        } else {/* INFO_EXT */
 410                *pages = diag204((unsigned long)SUBC_RSI |
 411                                 (unsigned long)INFO_EXT, 0, NULL);
 412                if (*pages <= 0)
 413                        return ERR_PTR(-ENOSYS);
 414                else
 415                        return diag204_alloc_vbuf(*pages);
 416        }
 417}
 418
 419/*
 420 * diag204_probe() has to find out, which type of diagnose 204 implementation
 421 * we have on our machine. Currently there are three possible scanarios:
 422 *   - subcode 4   + simple data format (only one page)
 423 *   - subcode 4-6 + extended data format
 424 *   - subcode 4-7 + extended data format
 425 *
 426 * Subcode 5 is used to retrieve the size of the data, provided by subcodes
 427 * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition
 428 * to subcode 6 it provides also information about secondary cpus.
 429 * In order to get as much information as possible, we first try
 430 * subcode 7, then 6 and if both fail, we use subcode 4.
 431 */
 432
 433static int diag204_probe(void)
 434{
 435        void *buf;
 436        int pages, rc;
 437
 438        buf = diag204_get_buffer(INFO_EXT, &pages);
 439        if (!IS_ERR(buf)) {
 440                if (diag204((unsigned long)SUBC_STIB7 |
 441                            (unsigned long)INFO_EXT, pages, buf) >= 0) {
 442                        diag204_store_sc = SUBC_STIB7;
 443                        diag204_info_type = INFO_EXT;
 444                        goto out;
 445                }
 446                if (diag204((unsigned long)SUBC_STIB6 |
 447                            (unsigned long)INFO_EXT, pages, buf) >= 0) {
 448                        diag204_store_sc = SUBC_STIB6;
 449                        diag204_info_type = INFO_EXT;
 450                        goto out;
 451                }
 452                diag204_free_buffer();
 453        }
 454
 455        /* subcodes 6 and 7 failed, now try subcode 4 */
 456
 457        buf = diag204_get_buffer(INFO_SIMPLE, &pages);
 458        if (IS_ERR(buf)) {
 459                rc = PTR_ERR(buf);
 460                goto fail_alloc;
 461        }
 462        if (diag204((unsigned long)SUBC_STIB4 |
 463                    (unsigned long)INFO_SIMPLE, pages, buf) >= 0) {
 464                diag204_store_sc = SUBC_STIB4;
 465                diag204_info_type = INFO_SIMPLE;
 466                goto out;
 467        } else {
 468                rc = -ENOSYS;
 469                goto fail_store;
 470        }
 471out:
 472        rc = 0;
 473fail_store:
 474        diag204_free_buffer();
 475fail_alloc:
 476        return rc;
 477}
 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
 488static void *diag204_store(void)
 489{
 490        void *buf;
 491        int pages, rc;
 492
 493        buf = diag204_get_buffer(diag204_info_type, &pages);
 494        if (IS_ERR(buf))
 495                goto out;
 496        rc = diag204_do_store(buf, pages);
 497        if (rc)
 498                return ERR_PTR(rc);
 499out:
 500        return buf;
 501}
 502
 503/* Diagnose 224 functions */
 504
 505static int diag224(void *ptr)
 506{
 507        int rc = -EOPNOTSUPP;
 508
 509        asm volatile(
 510                "       diag    %1,%2,0x224\n"
 511                "0:     lhi     %0,0x0\n"
 512                "1:\n"
 513                EX_TABLE(0b,1b)
 514                : "+d" (rc) :"d" (0), "d" (ptr) : "memory");
 515        return rc;
 516}
 517
 518static int diag224_get_name_table(void)
 519{
 520        /* memory must be below 2GB */
 521        diag224_cpu_names = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
 522        if (!diag224_cpu_names)
 523                return -ENOMEM;
 524        if (diag224(diag224_cpu_names)) {
 525                kfree(diag224_cpu_names);
 526                return -EOPNOTSUPP;
 527        }
 528        EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16);
 529        return 0;
 530}
 531
 532static void diag224_delete_name_table(void)
 533{
 534        kfree(diag224_cpu_names);
 535}
 536
 537static int diag224_idx2name(int index, char *name)
 538{
 539        memcpy(name, diag224_cpu_names + ((index + 1) * CPU_NAME_LEN),
 540                CPU_NAME_LEN);
 541        name[CPU_NAME_LEN] = 0;
 542        strim(name);
 543        return 0;
 544}
 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
 558static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size)
 559{
 560        struct dbfs_d204 *d204;
 561        int rc, buf_size;
 562        void *base;
 563
 564        buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr);
 565        base = vmalloc(buf_size);
 566        if (!base)
 567                return -ENOMEM;
 568        memset(base, 0, buf_size);
 569        d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr);
 570        rc = diag204_do_store(d204->buf, diag204_buf_pages);
 571        if (rc) {
 572                vfree(base);
 573                return rc;
 574        }
 575        d204->hdr.version = DBFS_D204_HDR_VERSION;
 576        d204->hdr.len = PAGE_SIZE * diag204_buf_pages;
 577        d204->hdr.sc = diag204_store_sc;
 578        *data = d204;
 579        *data_free_ptr = base;
 580        *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr);
 581        return 0;
 582}
 583
 584static struct hypfs_dbfs_file dbfs_file_d204 = {
 585        .name           = "diag_204",
 586        .data_create    = dbfs_d204_create,
 587        .data_free      = vfree,
 588};
 589
 590__init int hypfs_diag_init(void)
 591{
 592        int rc;
 593
 594        if (diag204_probe()) {
 595                pr_err("The hardware system does not support hypfs\n");
 596                return -ENODATA;
 597        }
 598        if (diag204_info_type == INFO_EXT) {
 599                rc = hypfs_dbfs_create_file(&dbfs_file_d204);
 600                if (rc)
 601                        return rc;
 602        }
 603        if (MACHINE_IS_LPAR) {
 604                rc = diag224_get_name_table();
 605                if (rc) {
 606                        pr_err("The hardware system does not provide all "
 607                               "functions required by hypfs\n");
 608                        debugfs_remove(dbfs_d204_file);
 609                        return rc;
 610                }
 611        }
 612        return 0;
 613}
 614
 615void hypfs_diag_exit(void)
 616{
 617        debugfs_remove(dbfs_d204_file);
 618        diag224_delete_name_table();
 619        diag204_free_buffer();
 620        hypfs_dbfs_remove_file(&dbfs_file_d204);
 621}
 622
 623/*
 624 * Functions to create the directory structure
 625 * *******************************************
 626 */
 627
 628static int hypfs_create_cpu_files(struct super_block *sb,
 629                                  struct dentry *cpus_dir, void *cpu_info)
 630{
 631        struct dentry *cpu_dir;
 632        char buffer[TMP_SIZE];
 633        void *rc;
 634
 635        snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type,
 636                                                            cpu_info));
 637        cpu_dir = hypfs_mkdir(sb, cpus_dir, buffer);
 638        rc = hypfs_create_u64(sb, cpu_dir, "mgmtime",
 639                              cpu_info__acc_time(diag204_info_type, cpu_info) -
 640                              cpu_info__lp_time(diag204_info_type, cpu_info));
 641        if (IS_ERR(rc))
 642                return PTR_ERR(rc);
 643        rc = hypfs_create_u64(sb, cpu_dir, "cputime",
 644                              cpu_info__lp_time(diag204_info_type, cpu_info));
 645        if (IS_ERR(rc))
 646                return PTR_ERR(rc);
 647        if (diag204_info_type == INFO_EXT) {
 648                rc = hypfs_create_u64(sb, cpu_dir, "onlinetime",
 649                                      cpu_info__online_time(diag204_info_type,
 650                                                            cpu_info));
 651                if (IS_ERR(rc))
 652                        return PTR_ERR(rc);
 653        }
 654        diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer);
 655        rc = hypfs_create_str(sb, cpu_dir, "type", buffer);
 656        if (IS_ERR(rc))
 657                return PTR_ERR(rc);
 658        return 0;
 659}
 660
 661static void *hypfs_create_lpar_files(struct super_block *sb,
 662                                     struct dentry *systems_dir, void *part_hdr)
 663{
 664        struct dentry *cpus_dir;
 665        struct dentry *lpar_dir;
 666        char lpar_name[LPAR_NAME_LEN + 1];
 667        void *cpu_info;
 668        int i;
 669
 670        part_hdr__part_name(diag204_info_type, part_hdr, lpar_name);
 671        lpar_name[LPAR_NAME_LEN] = 0;
 672        lpar_dir = hypfs_mkdir(sb, systems_dir, lpar_name);
 673        if (IS_ERR(lpar_dir))
 674                return lpar_dir;
 675        cpus_dir = hypfs_mkdir(sb, lpar_dir, "cpus");
 676        if (IS_ERR(cpus_dir))
 677                return cpus_dir;
 678        cpu_info = part_hdr + part_hdr__size(diag204_info_type);
 679        for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) {
 680                int rc;
 681                rc = hypfs_create_cpu_files(sb, cpus_dir, cpu_info);
 682                if (rc)
 683                        return ERR_PTR(rc);
 684                cpu_info += cpu_info__size(diag204_info_type);
 685        }
 686        return cpu_info;
 687}
 688
 689static int hypfs_create_phys_cpu_files(struct super_block *sb,
 690                                       struct dentry *cpus_dir, void *cpu_info)
 691{
 692        struct dentry *cpu_dir;
 693        char buffer[TMP_SIZE];
 694        void *rc;
 695
 696        snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type,
 697                                                            cpu_info));
 698        cpu_dir = hypfs_mkdir(sb, cpus_dir, buffer);
 699        if (IS_ERR(cpu_dir))
 700                return PTR_ERR(cpu_dir);
 701        rc = hypfs_create_u64(sb, cpu_dir, "mgmtime",
 702                              phys_cpu__mgm_time(diag204_info_type, cpu_info));
 703        if (IS_ERR(rc))
 704                return PTR_ERR(rc);
 705        diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer);
 706        rc = hypfs_create_str(sb, cpu_dir, "type", buffer);
 707        if (IS_ERR(rc))
 708                return PTR_ERR(rc);
 709        return 0;
 710}
 711
 712static void *hypfs_create_phys_files(struct super_block *sb,
 713                                     struct dentry *parent_dir, void *phys_hdr)
 714{
 715        int i;
 716        void *cpu_info;
 717        struct dentry *cpus_dir;
 718
 719        cpus_dir = hypfs_mkdir(sb, parent_dir, "cpus");
 720        if (IS_ERR(cpus_dir))
 721                return cpus_dir;
 722        cpu_info = phys_hdr + phys_hdr__size(diag204_info_type);
 723        for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) {
 724                int rc;
 725                rc = hypfs_create_phys_cpu_files(sb, cpus_dir, cpu_info);
 726                if (rc)
 727                        return ERR_PTR(rc);
 728                cpu_info += phys_cpu__size(diag204_info_type);
 729        }
 730        return cpu_info;
 731}
 732
 733int hypfs_diag_create_files(struct super_block *sb, struct dentry *root)
 734{
 735        struct dentry *systems_dir, *hyp_dir;
 736        void *time_hdr, *part_hdr;
 737        int i, rc;
 738        void *buffer, *ptr;
 739
 740        buffer = diag204_store();
 741        if (IS_ERR(buffer))
 742                return PTR_ERR(buffer);
 743
 744        systems_dir = hypfs_mkdir(sb, root, "systems");
 745        if (IS_ERR(systems_dir)) {
 746                rc = PTR_ERR(systems_dir);
 747                goto err_out;
 748        }
 749        time_hdr = (struct x_info_blk_hdr *)buffer;
 750        part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type);
 751        for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) {
 752                part_hdr = hypfs_create_lpar_files(sb, systems_dir, part_hdr);
 753                if (IS_ERR(part_hdr)) {
 754                        rc = PTR_ERR(part_hdr);
 755                        goto err_out;
 756                }
 757        }
 758        if (info_blk_hdr__flags(diag204_info_type, time_hdr) & LPAR_PHYS_FLG) {
 759                ptr = hypfs_create_phys_files(sb, root, part_hdr);
 760                if (IS_ERR(ptr)) {
 761                        rc = PTR_ERR(ptr);
 762                        goto err_out;
 763                }
 764        }
 765        hyp_dir = hypfs_mkdir(sb, root, "hyp");
 766        if (IS_ERR(hyp_dir)) {
 767                rc = PTR_ERR(hyp_dir);
 768                goto err_out;
 769        }
 770        ptr = hypfs_create_str(sb, hyp_dir, "type", "LPAR Hypervisor");
 771        if (IS_ERR(ptr)) {
 772                rc = PTR_ERR(ptr);
 773                goto err_out;
 774        }
 775        rc = 0;
 776
 777err_out:
 778        return rc;
 779}
 780