hypfs_diag.c (16577B)
1// SPDX-License-Identifier: GPL-2.0 2/* 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/diag.h> 20#include <asm/ebcdic.h> 21#include "hypfs.h" 22 23#define TMP_SIZE 64 /* size of temporary buffers */ 24 25#define DBFS_D204_HDR_VERSION 0 26 27static char *diag224_cpu_names; /* diag 224 name table */ 28static enum diag204_sc diag204_store_sc; /* used subcode for store */ 29static enum diag204_format diag204_info_type; /* used diag 204 data format */ 30 31static void *diag204_buf; /* 4K aligned buffer for diag204 data */ 32static void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */ 33static int diag204_buf_pages; /* number of pages for diag204 data */ 34 35static struct dentry *dbfs_d204_file; 36 37/* 38 * DIAG 204 member access functions. 39 * 40 * Since we have two different diag 204 data formats for old and new s390 41 * machines, we do not access the structs directly, but use getter functions for 42 * each struct member instead. This should make the code more readable. 43 */ 44 45/* Time information block */ 46 47static inline int info_blk_hdr__size(enum diag204_format type) 48{ 49 if (type == DIAG204_INFO_SIMPLE) 50 return sizeof(struct diag204_info_blk_hdr); 51 else /* DIAG204_INFO_EXT */ 52 return sizeof(struct diag204_x_info_blk_hdr); 53} 54 55static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr) 56{ 57 if (type == DIAG204_INFO_SIMPLE) 58 return ((struct diag204_info_blk_hdr *)hdr)->npar; 59 else /* DIAG204_INFO_EXT */ 60 return ((struct diag204_x_info_blk_hdr *)hdr)->npar; 61} 62 63static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr) 64{ 65 if (type == DIAG204_INFO_SIMPLE) 66 return ((struct diag204_info_blk_hdr *)hdr)->flags; 67 else /* DIAG204_INFO_EXT */ 68 return ((struct diag204_x_info_blk_hdr *)hdr)->flags; 69} 70 71static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr) 72{ 73 if (type == DIAG204_INFO_SIMPLE) 74 return ((struct diag204_info_blk_hdr *)hdr)->phys_cpus; 75 else /* DIAG204_INFO_EXT */ 76 return ((struct diag204_x_info_blk_hdr *)hdr)->phys_cpus; 77} 78 79/* Partition header */ 80 81static inline int part_hdr__size(enum diag204_format type) 82{ 83 if (type == DIAG204_INFO_SIMPLE) 84 return sizeof(struct diag204_part_hdr); 85 else /* DIAG204_INFO_EXT */ 86 return sizeof(struct diag204_x_part_hdr); 87} 88 89static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr) 90{ 91 if (type == DIAG204_INFO_SIMPLE) 92 return ((struct diag204_part_hdr *)hdr)->cpus; 93 else /* DIAG204_INFO_EXT */ 94 return ((struct diag204_x_part_hdr *)hdr)->rcpus; 95} 96 97static inline void part_hdr__part_name(enum diag204_format type, void *hdr, 98 char *name) 99{ 100 if (type == DIAG204_INFO_SIMPLE) 101 memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name, 102 DIAG204_LPAR_NAME_LEN); 103 else /* DIAG204_INFO_EXT */ 104 memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name, 105 DIAG204_LPAR_NAME_LEN); 106 EBCASC(name, DIAG204_LPAR_NAME_LEN); 107 name[DIAG204_LPAR_NAME_LEN] = 0; 108 strim(name); 109} 110 111/* CPU info block */ 112 113static inline int cpu_info__size(enum diag204_format type) 114{ 115 if (type == DIAG204_INFO_SIMPLE) 116 return sizeof(struct diag204_cpu_info); 117 else /* DIAG204_INFO_EXT */ 118 return sizeof(struct diag204_x_cpu_info); 119} 120 121static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr) 122{ 123 if (type == DIAG204_INFO_SIMPLE) 124 return ((struct diag204_cpu_info *)hdr)->ctidx; 125 else /* DIAG204_INFO_EXT */ 126 return ((struct diag204_x_cpu_info *)hdr)->ctidx; 127} 128 129static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr) 130{ 131 if (type == DIAG204_INFO_SIMPLE) 132 return ((struct diag204_cpu_info *)hdr)->cpu_addr; 133 else /* DIAG204_INFO_EXT */ 134 return ((struct diag204_x_cpu_info *)hdr)->cpu_addr; 135} 136 137static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr) 138{ 139 if (type == DIAG204_INFO_SIMPLE) 140 return ((struct diag204_cpu_info *)hdr)->acc_time; 141 else /* DIAG204_INFO_EXT */ 142 return ((struct diag204_x_cpu_info *)hdr)->acc_time; 143} 144 145static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr) 146{ 147 if (type == DIAG204_INFO_SIMPLE) 148 return ((struct diag204_cpu_info *)hdr)->lp_time; 149 else /* DIAG204_INFO_EXT */ 150 return ((struct diag204_x_cpu_info *)hdr)->lp_time; 151} 152 153static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr) 154{ 155 if (type == DIAG204_INFO_SIMPLE) 156 return 0; /* online_time not available in simple info */ 157 else /* DIAG204_INFO_EXT */ 158 return ((struct diag204_x_cpu_info *)hdr)->online_time; 159} 160 161/* Physical header */ 162 163static inline int phys_hdr__size(enum diag204_format type) 164{ 165 if (type == DIAG204_INFO_SIMPLE) 166 return sizeof(struct diag204_phys_hdr); 167 else /* DIAG204_INFO_EXT */ 168 return sizeof(struct diag204_x_phys_hdr); 169} 170 171static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr) 172{ 173 if (type == DIAG204_INFO_SIMPLE) 174 return ((struct diag204_phys_hdr *)hdr)->cpus; 175 else /* DIAG204_INFO_EXT */ 176 return ((struct diag204_x_phys_hdr *)hdr)->cpus; 177} 178 179/* Physical CPU info block */ 180 181static inline int phys_cpu__size(enum diag204_format type) 182{ 183 if (type == DIAG204_INFO_SIMPLE) 184 return sizeof(struct diag204_phys_cpu); 185 else /* DIAG204_INFO_EXT */ 186 return sizeof(struct diag204_x_phys_cpu); 187} 188 189static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr) 190{ 191 if (type == DIAG204_INFO_SIMPLE) 192 return ((struct diag204_phys_cpu *)hdr)->cpu_addr; 193 else /* DIAG204_INFO_EXT */ 194 return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr; 195} 196 197static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr) 198{ 199 if (type == DIAG204_INFO_SIMPLE) 200 return ((struct diag204_phys_cpu *)hdr)->mgm_time; 201 else /* DIAG204_INFO_EXT */ 202 return ((struct diag204_x_phys_cpu *)hdr)->mgm_time; 203} 204 205static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr) 206{ 207 if (type == DIAG204_INFO_SIMPLE) 208 return ((struct diag204_phys_cpu *)hdr)->ctidx; 209 else /* DIAG204_INFO_EXT */ 210 return ((struct diag204_x_phys_cpu *)hdr)->ctidx; 211} 212 213/* Diagnose 204 functions */ 214/* 215 * For the old diag subcode 4 with simple data format we have to use real 216 * memory. If we use subcode 6 or 7 with extended data format, we can (and 217 * should) use vmalloc, since we need a lot of memory in that case. Currently 218 * up to 93 pages! 219 */ 220 221static void diag204_free_buffer(void) 222{ 223 if (!diag204_buf) 224 return; 225 if (diag204_buf_vmalloc) { 226 vfree(diag204_buf_vmalloc); 227 diag204_buf_vmalloc = NULL; 228 } else { 229 free_pages((unsigned long) diag204_buf, 0); 230 } 231 diag204_buf = NULL; 232} 233 234static void *page_align_ptr(void *ptr) 235{ 236 return (void *) PAGE_ALIGN((unsigned long) ptr); 237} 238 239static void *diag204_alloc_vbuf(int pages) 240{ 241 /* The buffer has to be page aligned! */ 242 diag204_buf_vmalloc = vmalloc(array_size(PAGE_SIZE, (pages + 1))); 243 if (!diag204_buf_vmalloc) 244 return ERR_PTR(-ENOMEM); 245 diag204_buf = page_align_ptr(diag204_buf_vmalloc); 246 diag204_buf_pages = pages; 247 return diag204_buf; 248} 249 250static void *diag204_alloc_rbuf(void) 251{ 252 diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0); 253 if (!diag204_buf) 254 return ERR_PTR(-ENOMEM); 255 diag204_buf_pages = 1; 256 return diag204_buf; 257} 258 259static void *diag204_get_buffer(enum diag204_format fmt, int *pages) 260{ 261 if (diag204_buf) { 262 *pages = diag204_buf_pages; 263 return diag204_buf; 264 } 265 if (fmt == DIAG204_INFO_SIMPLE) { 266 *pages = 1; 267 return diag204_alloc_rbuf(); 268 } else {/* DIAG204_INFO_EXT */ 269 *pages = diag204((unsigned long)DIAG204_SUBC_RSI | 270 (unsigned long)DIAG204_INFO_EXT, 0, NULL); 271 if (*pages <= 0) 272 return ERR_PTR(-ENOSYS); 273 else 274 return diag204_alloc_vbuf(*pages); 275 } 276} 277 278/* 279 * diag204_probe() has to find out, which type of diagnose 204 implementation 280 * we have on our machine. Currently there are three possible scanarios: 281 * - subcode 4 + simple data format (only one page) 282 * - subcode 4-6 + extended data format 283 * - subcode 4-7 + extended data format 284 * 285 * Subcode 5 is used to retrieve the size of the data, provided by subcodes 286 * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition 287 * to subcode 6 it provides also information about secondary cpus. 288 * In order to get as much information as possible, we first try 289 * subcode 7, then 6 and if both fail, we use subcode 4. 290 */ 291 292static int diag204_probe(void) 293{ 294 void *buf; 295 int pages, rc; 296 297 buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages); 298 if (!IS_ERR(buf)) { 299 if (diag204((unsigned long)DIAG204_SUBC_STIB7 | 300 (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 301 diag204_store_sc = DIAG204_SUBC_STIB7; 302 diag204_info_type = DIAG204_INFO_EXT; 303 goto out; 304 } 305 if (diag204((unsigned long)DIAG204_SUBC_STIB6 | 306 (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 307 diag204_store_sc = DIAG204_SUBC_STIB6; 308 diag204_info_type = DIAG204_INFO_EXT; 309 goto out; 310 } 311 diag204_free_buffer(); 312 } 313 314 /* subcodes 6 and 7 failed, now try subcode 4 */ 315 316 buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages); 317 if (IS_ERR(buf)) { 318 rc = PTR_ERR(buf); 319 goto fail_alloc; 320 } 321 if (diag204((unsigned long)DIAG204_SUBC_STIB4 | 322 (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) { 323 diag204_store_sc = DIAG204_SUBC_STIB4; 324 diag204_info_type = DIAG204_INFO_SIMPLE; 325 goto out; 326 } else { 327 rc = -ENOSYS; 328 goto fail_store; 329 } 330out: 331 rc = 0; 332fail_store: 333 diag204_free_buffer(); 334fail_alloc: 335 return rc; 336} 337 338static int diag204_do_store(void *buf, int pages) 339{ 340 int rc; 341 342 rc = diag204((unsigned long) diag204_store_sc | 343 (unsigned long) diag204_info_type, pages, buf); 344 return rc < 0 ? -ENOSYS : 0; 345} 346 347static void *diag204_store(void) 348{ 349 void *buf; 350 int pages, rc; 351 352 buf = diag204_get_buffer(diag204_info_type, &pages); 353 if (IS_ERR(buf)) 354 goto out; 355 rc = diag204_do_store(buf, pages); 356 if (rc) 357 return ERR_PTR(rc); 358out: 359 return buf; 360} 361 362/* Diagnose 224 functions */ 363 364static int diag224_get_name_table(void) 365{ 366 /* memory must be below 2GB */ 367 diag224_cpu_names = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); 368 if (!diag224_cpu_names) 369 return -ENOMEM; 370 if (diag224(diag224_cpu_names)) { 371 free_page((unsigned long) diag224_cpu_names); 372 return -EOPNOTSUPP; 373 } 374 EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16); 375 return 0; 376} 377 378static void diag224_delete_name_table(void) 379{ 380 free_page((unsigned long) diag224_cpu_names); 381} 382 383static int diag224_idx2name(int index, char *name) 384{ 385 memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN), 386 DIAG204_CPU_NAME_LEN); 387 name[DIAG204_CPU_NAME_LEN] = 0; 388 strim(name); 389 return 0; 390} 391 392struct dbfs_d204_hdr { 393 u64 len; /* Length of d204 buffer without header */ 394 u16 version; /* Version of header */ 395 u8 sc; /* Used subcode */ 396 char reserved[53]; 397} __attribute__ ((packed)); 398 399struct dbfs_d204 { 400 struct dbfs_d204_hdr hdr; /* 64 byte header */ 401 char buf[]; /* d204 buffer */ 402} __attribute__ ((packed)); 403 404static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 405{ 406 struct dbfs_d204 *d204; 407 int rc, buf_size; 408 void *base; 409 410 buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 411 base = vzalloc(buf_size); 412 if (!base) 413 return -ENOMEM; 414 d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr); 415 rc = diag204_do_store(d204->buf, diag204_buf_pages); 416 if (rc) { 417 vfree(base); 418 return rc; 419 } 420 d204->hdr.version = DBFS_D204_HDR_VERSION; 421 d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 422 d204->hdr.sc = diag204_store_sc; 423 *data = d204; 424 *data_free_ptr = base; 425 *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 426 return 0; 427} 428 429static struct hypfs_dbfs_file dbfs_file_d204 = { 430 .name = "diag_204", 431 .data_create = dbfs_d204_create, 432 .data_free = vfree, 433}; 434 435__init int hypfs_diag_init(void) 436{ 437 int rc; 438 439 if (diag204_probe()) { 440 pr_err("The hardware system does not support hypfs\n"); 441 return -ENODATA; 442 } 443 444 if (diag204_info_type == DIAG204_INFO_EXT) 445 hypfs_dbfs_create_file(&dbfs_file_d204); 446 447 if (MACHINE_IS_LPAR) { 448 rc = diag224_get_name_table(); 449 if (rc) { 450 pr_err("The hardware system does not provide all " 451 "functions required by hypfs\n"); 452 debugfs_remove(dbfs_d204_file); 453 return rc; 454 } 455 } 456 return 0; 457} 458 459void hypfs_diag_exit(void) 460{ 461 debugfs_remove(dbfs_d204_file); 462 diag224_delete_name_table(); 463 diag204_free_buffer(); 464 hypfs_dbfs_remove_file(&dbfs_file_d204); 465} 466 467/* 468 * Functions to create the directory structure 469 * ******************************************* 470 */ 471 472static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info) 473{ 474 struct dentry *cpu_dir; 475 char buffer[TMP_SIZE]; 476 void *rc; 477 478 snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type, 479 cpu_info)); 480 cpu_dir = hypfs_mkdir(cpus_dir, buffer); 481 rc = hypfs_create_u64(cpu_dir, "mgmtime", 482 cpu_info__acc_time(diag204_info_type, cpu_info) - 483 cpu_info__lp_time(diag204_info_type, cpu_info)); 484 if (IS_ERR(rc)) 485 return PTR_ERR(rc); 486 rc = hypfs_create_u64(cpu_dir, "cputime", 487 cpu_info__lp_time(diag204_info_type, cpu_info)); 488 if (IS_ERR(rc)) 489 return PTR_ERR(rc); 490 if (diag204_info_type == DIAG204_INFO_EXT) { 491 rc = hypfs_create_u64(cpu_dir, "onlinetime", 492 cpu_info__online_time(diag204_info_type, 493 cpu_info)); 494 if (IS_ERR(rc)) 495 return PTR_ERR(rc); 496 } 497 diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer); 498 rc = hypfs_create_str(cpu_dir, "type", buffer); 499 return PTR_ERR_OR_ZERO(rc); 500} 501 502static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr) 503{ 504 struct dentry *cpus_dir; 505 struct dentry *lpar_dir; 506 char lpar_name[DIAG204_LPAR_NAME_LEN + 1]; 507 void *cpu_info; 508 int i; 509 510 part_hdr__part_name(diag204_info_type, part_hdr, lpar_name); 511 lpar_name[DIAG204_LPAR_NAME_LEN] = 0; 512 lpar_dir = hypfs_mkdir(systems_dir, lpar_name); 513 if (IS_ERR(lpar_dir)) 514 return lpar_dir; 515 cpus_dir = hypfs_mkdir(lpar_dir, "cpus"); 516 if (IS_ERR(cpus_dir)) 517 return cpus_dir; 518 cpu_info = part_hdr + part_hdr__size(diag204_info_type); 519 for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) { 520 int rc; 521 rc = hypfs_create_cpu_files(cpus_dir, cpu_info); 522 if (rc) 523 return ERR_PTR(rc); 524 cpu_info += cpu_info__size(diag204_info_type); 525 } 526 return cpu_info; 527} 528 529static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info) 530{ 531 struct dentry *cpu_dir; 532 char buffer[TMP_SIZE]; 533 void *rc; 534 535 snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type, 536 cpu_info)); 537 cpu_dir = hypfs_mkdir(cpus_dir, buffer); 538 if (IS_ERR(cpu_dir)) 539 return PTR_ERR(cpu_dir); 540 rc = hypfs_create_u64(cpu_dir, "mgmtime", 541 phys_cpu__mgm_time(diag204_info_type, cpu_info)); 542 if (IS_ERR(rc)) 543 return PTR_ERR(rc); 544 diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer); 545 rc = hypfs_create_str(cpu_dir, "type", buffer); 546 return PTR_ERR_OR_ZERO(rc); 547} 548 549static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr) 550{ 551 int i; 552 void *cpu_info; 553 struct dentry *cpus_dir; 554 555 cpus_dir = hypfs_mkdir(parent_dir, "cpus"); 556 if (IS_ERR(cpus_dir)) 557 return cpus_dir; 558 cpu_info = phys_hdr + phys_hdr__size(diag204_info_type); 559 for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) { 560 int rc; 561 rc = hypfs_create_phys_cpu_files(cpus_dir, cpu_info); 562 if (rc) 563 return ERR_PTR(rc); 564 cpu_info += phys_cpu__size(diag204_info_type); 565 } 566 return cpu_info; 567} 568 569int hypfs_diag_create_files(struct dentry *root) 570{ 571 struct dentry *systems_dir, *hyp_dir; 572 void *time_hdr, *part_hdr; 573 int i, rc; 574 void *buffer, *ptr; 575 576 buffer = diag204_store(); 577 if (IS_ERR(buffer)) 578 return PTR_ERR(buffer); 579 580 systems_dir = hypfs_mkdir(root, "systems"); 581 if (IS_ERR(systems_dir)) { 582 rc = PTR_ERR(systems_dir); 583 goto err_out; 584 } 585 time_hdr = (struct x_info_blk_hdr *)buffer; 586 part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type); 587 for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) { 588 part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr); 589 if (IS_ERR(part_hdr)) { 590 rc = PTR_ERR(part_hdr); 591 goto err_out; 592 } 593 } 594 if (info_blk_hdr__flags(diag204_info_type, time_hdr) & 595 DIAG204_LPAR_PHYS_FLG) { 596 ptr = hypfs_create_phys_files(root, part_hdr); 597 if (IS_ERR(ptr)) { 598 rc = PTR_ERR(ptr); 599 goto err_out; 600 } 601 } 602 hyp_dir = hypfs_mkdir(root, "hyp"); 603 if (IS_ERR(hyp_dir)) { 604 rc = PTR_ERR(hyp_dir); 605 goto err_out; 606 } 607 ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor"); 608 if (IS_ERR(ptr)) { 609 rc = PTR_ERR(ptr); 610 goto err_out; 611 } 612 rc = 0; 613 614err_out: 615 return rc; 616}