| .. | .. |
|---|
| 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 | |
|---|
| .. | .. |
|---|
| 136 | 134 | ppp_data->entitled_proc_cap_avail = retbuf[4] & 0xffffff; |
|---|
| 137 | 135 | |
|---|
| 138 | 136 | return rc; |
|---|
| 137 | +} |
|---|
| 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); |
|---|
| 139 | 170 | } |
|---|
| 140 | 171 | |
|---|
| 141 | 172 | static unsigned h_pic(unsigned long *pool_idle_time, |
|---|
| .. | .. |
|---|
| 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 | |
|---|
| .. | .. |
|---|
| 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 | } |
|---|