| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * PowerPC64 LPAR Configuration Information Driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 8 | 9 | * seq_file updates, Copyright (c) 2004 Will Schmidt IBM Corporation. |
|---|
| 9 | 10 | * Nathan Lynch nathanl@austin.ibm.com |
|---|
| 10 | 11 | * Added lparcfg_write, Copyright (C) 2004 Nathan Lynch IBM Corporation. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is free software; you can redistribute it and/or |
|---|
| 13 | | - * modify it under the terms of the GNU General Public License |
|---|
| 14 | | - * as published by the Free Software Foundation; either version |
|---|
| 15 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 16 | 12 | * |
|---|
| 17 | 13 | * This driver creates a proc file at /proc/ppc64/lparcfg which contains |
|---|
| 18 | 14 | * keyword - value pairs that specify the configuration of the partition. |
|---|
| .. | .. |
|---|
| 26 | 22 | #include <linux/seq_file.h> |
|---|
| 27 | 23 | #include <linux/slab.h> |
|---|
| 28 | 24 | #include <linux/uaccess.h> |
|---|
| 25 | +#include <linux/hugetlb.h> |
|---|
| 29 | 26 | #include <asm/lppaca.h> |
|---|
| 30 | 27 | #include <asm/hvcall.h> |
|---|
| 31 | 28 | #include <asm/firmware.h> |
|---|
| .. | .. |
|---|
| 36 | 33 | #include <asm/vio.h> |
|---|
| 37 | 34 | #include <asm/mmu.h> |
|---|
| 38 | 35 | #include <asm/machdep.h> |
|---|
| 36 | +#include <asm/drmem.h> |
|---|
| 39 | 37 | |
|---|
| 40 | 38 | #include "pseries.h" |
|---|
| 41 | 39 | |
|---|
| .. | .. |
|---|
| 138 | 136 | return rc; |
|---|
| 139 | 137 | } |
|---|
| 140 | 138 | |
|---|
| 139 | +static void show_gpci_data(struct seq_file *m) |
|---|
| 140 | +{ |
|---|
| 141 | + struct hv_gpci_request_buffer *buf; |
|---|
| 142 | + unsigned int affinity_score; |
|---|
| 143 | + long ret; |
|---|
| 144 | + |
|---|
| 145 | + buf = kmalloc(sizeof(*buf), GFP_KERNEL); |
|---|
| 146 | + if (buf == NULL) |
|---|
| 147 | + return; |
|---|
| 148 | + |
|---|
| 149 | + /* |
|---|
| 150 | + * Show the local LPAR's affinity score. |
|---|
| 151 | + * |
|---|
| 152 | + * 0xB1 selects the Affinity_Domain_Info_By_Partition subcall. |
|---|
| 153 | + * The score is at byte 0xB in the output buffer. |
|---|
| 154 | + */ |
|---|
| 155 | + memset(&buf->params, 0, sizeof(buf->params)); |
|---|
| 156 | + buf->params.counter_request = cpu_to_be32(0xB1); |
|---|
| 157 | + buf->params.starting_index = cpu_to_be32(-1); /* local LPAR */ |
|---|
| 158 | + buf->params.counter_info_version_in = 0x5; /* v5+ for score */ |
|---|
| 159 | + ret = plpar_hcall_norets(H_GET_PERF_COUNTER_INFO, virt_to_phys(buf), |
|---|
| 160 | + sizeof(*buf)); |
|---|
| 161 | + if (ret != H_SUCCESS) { |
|---|
| 162 | + pr_debug("hcall failed: H_GET_PERF_COUNTER_INFO: %ld, %x\n", |
|---|
| 163 | + ret, be32_to_cpu(buf->params.detail_rc)); |
|---|
| 164 | + goto out; |
|---|
| 165 | + } |
|---|
| 166 | + affinity_score = buf->bytes[0xB]; |
|---|
| 167 | + seq_printf(m, "partition_affinity_score=%u\n", affinity_score); |
|---|
| 168 | +out: |
|---|
| 169 | + kfree(buf); |
|---|
| 170 | +} |
|---|
| 171 | + |
|---|
| 141 | 172 | static unsigned h_pic(unsigned long *pool_idle_time, |
|---|
| 142 | 173 | unsigned long *num_procs) |
|---|
| 143 | 174 | { |
|---|
| .. | .. |
|---|
| 174 | 205 | ppp_data.active_system_procs); |
|---|
| 175 | 206 | |
|---|
| 176 | 207 | /* pool related entries are appropriate for shared configs */ |
|---|
| 177 | | - if (lppaca_shared_proc(get_lppaca())) { |
|---|
| 208 | + if (lppaca_shared_proc()) { |
|---|
| 178 | 209 | unsigned long pool_idle_time, pool_procs; |
|---|
| 179 | 210 | |
|---|
| 180 | 211 | seq_printf(m, "pool=%d\n", ppp_data.pool_num); |
|---|
| .. | .. |
|---|
| 291 | 322 | */ |
|---|
| 292 | 323 | static void parse_system_parameter_string(struct seq_file *m) |
|---|
| 293 | 324 | { |
|---|
| 325 | + const s32 token = rtas_token("ibm,get-system-parameter"); |
|---|
| 294 | 326 | int call_status; |
|---|
| 295 | 327 | |
|---|
| 296 | 328 | unsigned char *local_buffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 300 | 332 | return; |
|---|
| 301 | 333 | } |
|---|
| 302 | 334 | |
|---|
| 303 | | - spin_lock(&rtas_data_buf_lock); |
|---|
| 304 | | - memset(rtas_data_buf, 0, SPLPAR_MAXLENGTH); |
|---|
| 305 | | - call_status = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, |
|---|
| 306 | | - NULL, |
|---|
| 307 | | - SPLPAR_CHARACTERISTICS_TOKEN, |
|---|
| 308 | | - __pa(rtas_data_buf), |
|---|
| 309 | | - RTAS_DATA_BUF_SIZE); |
|---|
| 310 | | - memcpy(local_buffer, rtas_data_buf, SPLPAR_MAXLENGTH); |
|---|
| 311 | | - local_buffer[SPLPAR_MAXLENGTH - 1] = '\0'; |
|---|
| 312 | | - spin_unlock(&rtas_data_buf_lock); |
|---|
| 335 | + do { |
|---|
| 336 | + spin_lock(&rtas_data_buf_lock); |
|---|
| 337 | + memset(rtas_data_buf, 0, SPLPAR_MAXLENGTH); |
|---|
| 338 | + call_status = rtas_call(token, 3, 1, NULL, SPLPAR_CHARACTERISTICS_TOKEN, |
|---|
| 339 | + __pa(rtas_data_buf), RTAS_DATA_BUF_SIZE); |
|---|
| 340 | + memcpy(local_buffer, rtas_data_buf, SPLPAR_MAXLENGTH); |
|---|
| 341 | + local_buffer[SPLPAR_MAXLENGTH - 1] = '\0'; |
|---|
| 342 | + spin_unlock(&rtas_data_buf_lock); |
|---|
| 343 | + } while (rtas_busy_delay(call_status)); |
|---|
| 313 | 344 | |
|---|
| 314 | 345 | if (call_status != 0) { |
|---|
| 315 | 346 | printk(KERN_INFO |
|---|
| .. | .. |
|---|
| 433 | 464 | seq_printf(m, "power_mode_data=%016lx\n", retbuf[0]); |
|---|
| 434 | 465 | } |
|---|
| 435 | 466 | |
|---|
| 467 | +static void maxmem_data(struct seq_file *m) |
|---|
| 468 | +{ |
|---|
| 469 | + unsigned long maxmem = 0; |
|---|
| 470 | + |
|---|
| 471 | + maxmem += (unsigned long)drmem_info->n_lmbs * drmem_info->lmb_size; |
|---|
| 472 | + maxmem += hugetlb_total_pages() * PAGE_SIZE; |
|---|
| 473 | + |
|---|
| 474 | + seq_printf(m, "MaxMem=%lu\n", maxmem); |
|---|
| 475 | +} |
|---|
| 476 | + |
|---|
| 436 | 477 | static int pseries_lparcfg_data(struct seq_file *m, void *v) |
|---|
| 437 | 478 | { |
|---|
| 438 | 479 | int partition_potential_processors; |
|---|
| .. | .. |
|---|
| 463 | 504 | splpar_dispatch_data(m); |
|---|
| 464 | 505 | |
|---|
| 465 | 506 | seq_printf(m, "purr=%ld\n", get_purr()); |
|---|
| 507 | + seq_printf(m, "tbr=%ld\n", mftb()); |
|---|
| 466 | 508 | } else { /* non SPLPAR case */ |
|---|
| 467 | 509 | |
|---|
| 468 | 510 | seq_printf(m, "system_active_processors=%d\n", |
|---|
| .. | .. |
|---|
| 478 | 520 | partition_active_processors * 100); |
|---|
| 479 | 521 | } |
|---|
| 480 | 522 | |
|---|
| 523 | + show_gpci_data(m); |
|---|
| 524 | + |
|---|
| 481 | 525 | seq_printf(m, "partition_active_processors=%d\n", |
|---|
| 482 | 526 | partition_active_processors); |
|---|
| 483 | 527 | |
|---|
| .. | .. |
|---|
| 485 | 529 | partition_potential_processors); |
|---|
| 486 | 530 | |
|---|
| 487 | 531 | seq_printf(m, "shared_processor_mode=%d\n", |
|---|
| 488 | | - lppaca_shared_proc(get_lppaca())); |
|---|
| 532 | + lppaca_shared_proc()); |
|---|
| 489 | 533 | |
|---|
| 490 | 534 | #ifdef CONFIG_PPC_BOOK3S_64 |
|---|
| 491 | 535 | seq_printf(m, "slb_size=%d\n", mmu_slb_size); |
|---|
| 492 | 536 | #endif |
|---|
| 493 | 537 | parse_em_data(m); |
|---|
| 538 | + maxmem_data(m); |
|---|
| 494 | 539 | |
|---|
| 495 | 540 | return 0; |
|---|
| 496 | 541 | } |
|---|
| .. | .. |
|---|
| 585 | 630 | static ssize_t lparcfg_write(struct file *file, const char __user * buf, |
|---|
| 586 | 631 | size_t count, loff_t * off) |
|---|
| 587 | 632 | { |
|---|
| 588 | | - int kbuf_sz = 64; |
|---|
| 589 | | - char kbuf[kbuf_sz]; |
|---|
| 633 | + char kbuf[64]; |
|---|
| 590 | 634 | char *tmp; |
|---|
| 591 | 635 | u64 new_entitled, *new_entitled_ptr = &new_entitled; |
|---|
| 592 | 636 | u8 new_weight, *new_weight_ptr = &new_weight; |
|---|
| .. | .. |
|---|
| 595 | 639 | if (!firmware_has_feature(FW_FEATURE_SPLPAR)) |
|---|
| 596 | 640 | return -EINVAL; |
|---|
| 597 | 641 | |
|---|
| 598 | | - if (count > kbuf_sz) |
|---|
| 642 | + if (count > sizeof(kbuf)) |
|---|
| 599 | 643 | return -EINVAL; |
|---|
| 600 | 644 | |
|---|
| 601 | 645 | if (copy_from_user(kbuf, buf, count)) |
|---|
| .. | .. |
|---|
| 689 | 733 | return single_open(file, lparcfg_data, NULL); |
|---|
| 690 | 734 | } |
|---|
| 691 | 735 | |
|---|
| 692 | | -static const struct file_operations lparcfg_fops = { |
|---|
| 693 | | - .read = seq_read, |
|---|
| 694 | | - .write = lparcfg_write, |
|---|
| 695 | | - .open = lparcfg_open, |
|---|
| 696 | | - .release = single_release, |
|---|
| 697 | | - .llseek = seq_lseek, |
|---|
| 736 | +static const struct proc_ops lparcfg_proc_ops = { |
|---|
| 737 | + .proc_read = seq_read, |
|---|
| 738 | + .proc_write = lparcfg_write, |
|---|
| 739 | + .proc_open = lparcfg_open, |
|---|
| 740 | + .proc_release = single_release, |
|---|
| 741 | + .proc_lseek = seq_lseek, |
|---|
| 698 | 742 | }; |
|---|
| 699 | 743 | |
|---|
| 700 | 744 | static int __init lparcfg_init(void) |
|---|
| .. | .. |
|---|
| 705 | 749 | if (firmware_has_feature(FW_FEATURE_SPLPAR)) |
|---|
| 706 | 750 | mode |= 0200; |
|---|
| 707 | 751 | |
|---|
| 708 | | - if (!proc_create("powerpc/lparcfg", mode, NULL, &lparcfg_fops)) { |
|---|
| 752 | + if (!proc_create("powerpc/lparcfg", mode, NULL, &lparcfg_proc_ops)) { |
|---|
| 709 | 753 | printk(KERN_ERR "Failed to create powerpc/lparcfg\n"); |
|---|
| 710 | 754 | return -EIO; |
|---|
| 711 | 755 | } |
|---|