Path: blob/master/arch/powerpc/platforms/pseries/lparcfg.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* PowerPC64 LPAR Configuration Information Driver3*4* Dave Engebretsen [email protected]5* Copyright (c) 2003 Dave Engebretsen6* Will Schmidt [email protected]7* SPLPAR updates, Copyright (c) 2003 Will Schmidt IBM Corporation.8* seq_file updates, Copyright (c) 2004 Will Schmidt IBM Corporation.9* Nathan Lynch [email protected]10* Added lparcfg_write, Copyright (C) 2004 Nathan Lynch IBM Corporation.11*12* This driver creates a proc file at /proc/ppc64/lparcfg which contains13* keyword - value pairs that specify the configuration of the partition.14*/1516#include <linux/module.h>17#include <linux/types.h>18#include <linux/errno.h>19#include <linux/proc_fs.h>20#include <linux/init.h>21#include <asm/papr-sysparm.h>22#include <linux/seq_file.h>23#include <linux/slab.h>24#include <linux/uaccess.h>25#include <linux/hugetlb.h>26#include <asm/lppaca.h>27#include <asm/hvcall.h>28#include <asm/firmware.h>29#include <asm/rtas.h>30#include <asm/time.h>31#include <asm/vio.h>32#include <asm/mmu.h>33#include <asm/machdep.h>34#include <asm/drmem.h>3536#include "pseries.h"37#include "vas.h" /* pseries_vas_dlpar_cpu() */3839/*40* This isn't a module but we expose that to userspace41* via /proc so leave the definitions here42*/43#define MODULE_VERS "1.9"44#define MODULE_NAME "lparcfg"4546/* #define LPARCFG_DEBUG */4748/*49* Track sum of all purrs across all processors. This is used to further50* calculate usage values by different applications51*/52static void cpu_get_purr(void *arg)53{54atomic64_t *sum = arg;5556atomic64_add(mfspr(SPRN_PURR), sum);57}5859static unsigned long get_purr(void)60{61atomic64_t purr = ATOMIC64_INIT(0);6263on_each_cpu(cpu_get_purr, &purr, 1);6465return atomic64_read(&purr);66}6768/*69* Methods used to fetch LPAR data when running on a pSeries platform.70*/7172struct hvcall_ppp_data {73u64 entitlement;74u64 unallocated_entitlement;75u16 group_num;76u16 pool_num;77u8 capped;78u8 weight;79u8 unallocated_weight;80u16 active_procs_in_pool;81u16 active_system_procs;82u16 phys_platform_procs;83u32 max_proc_cap_avail;84u32 entitled_proc_cap_avail;85};8687/*88* H_GET_PPP hcall returns info in 4 parms.89* entitled_capacity,unallocated_capacity,90* aggregation, resource_capability).91*92* R4 = Entitled Processor Capacity Percentage.93* R5 = Unallocated Processor Capacity Percentage.94* R6 (AABBCCDDEEFFGGHH).95* XXXX - reserved (0)96* XXXX - reserved (0)97* XXXX - Group Number98* XXXX - Pool Number.99* R7 (IIJJKKLLMMNNOOPP).100* XX - reserved. (0)101* XX - bit 0-6 reserved (0). bit 7 is Capped indicator.102* XX - variable processor Capacity Weight103* XX - Unallocated Variable Processor Capacity Weight.104* XXXX - Active processors in Physical Processor Pool.105* XXXX - Processors active on platform.106* R8 (QQQQRRRRRRSSSSSS). if ibm,partition-performance-parameters-level >= 1107* XXXX - Physical platform procs allocated to virtualization.108* XXXXXX - Max procs capacity % available to the partitions pool.109* XXXXXX - Entitled procs capacity % available to the110* partitions pool.111*/112static unsigned int h_get_ppp(struct hvcall_ppp_data *ppp_data)113{114unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};115long rc;116117rc = plpar_hcall9(H_GET_PPP, retbuf);118119ppp_data->entitlement = retbuf[0];120ppp_data->unallocated_entitlement = retbuf[1];121122ppp_data->group_num = (retbuf[2] >> 2 * 8) & 0xffff;123ppp_data->pool_num = retbuf[2] & 0xffff;124125ppp_data->capped = (retbuf[3] >> 6 * 8) & 0x01;126ppp_data->weight = (retbuf[3] >> 5 * 8) & 0xff;127ppp_data->unallocated_weight = (retbuf[3] >> 4 * 8) & 0xff;128ppp_data->active_procs_in_pool = (retbuf[3] >> 2 * 8) & 0xffff;129ppp_data->active_system_procs = retbuf[3] & 0xffff;130131ppp_data->phys_platform_procs = retbuf[4] >> 6 * 8;132ppp_data->max_proc_cap_avail = (retbuf[4] >> 3 * 8) & 0xffffff;133ppp_data->entitled_proc_cap_avail = retbuf[4] & 0xffffff;134135return rc;136}137138static void show_gpci_data(struct seq_file *m)139{140struct hv_gpci_request_buffer *buf;141unsigned int affinity_score;142long ret;143144buf = kmalloc(sizeof(*buf), GFP_KERNEL);145if (buf == NULL)146return;147148/*149* Show the local LPAR's affinity score.150*151* 0xB1 selects the Affinity_Domain_Info_By_Partition subcall.152* The score is at byte 0xB in the output buffer.153*/154memset(&buf->params, 0, sizeof(buf->params));155buf->params.counter_request = cpu_to_be32(0xB1);156buf->params.starting_index = cpu_to_be32(-1); /* local LPAR */157buf->params.counter_info_version_in = 0x5; /* v5+ for score */158ret = plpar_hcall_norets(H_GET_PERF_COUNTER_INFO, virt_to_phys(buf),159sizeof(*buf));160if (ret != H_SUCCESS) {161pr_debug("hcall failed: H_GET_PERF_COUNTER_INFO: %ld, %x\n",162ret, be32_to_cpu(buf->params.detail_rc));163goto out;164}165affinity_score = buf->bytes[0xB];166seq_printf(m, "partition_affinity_score=%u\n", affinity_score);167out:168kfree(buf);169}170171static long h_pic(unsigned long *pool_idle_time,172unsigned long *num_procs)173{174long rc;175unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};176177rc = plpar_hcall(H_PIC, retbuf);178179if (pool_idle_time)180*pool_idle_time = retbuf[0];181if (num_procs)182*num_procs = retbuf[1];183184return rc;185}186187unsigned long boot_pool_idle_time;188189/*190* parse_ppp_data191* Parse out the data returned from h_get_ppp and h_pic192*/193static void parse_ppp_data(struct seq_file *m)194{195struct hvcall_ppp_data ppp_data;196struct device_node *root;197const __be32 *perf_level;198long rc;199200rc = h_get_ppp(&ppp_data);201if (rc)202return;203204seq_printf(m, "partition_entitled_capacity=%lld\n",205ppp_data.entitlement);206seq_printf(m, "group=%d\n", ppp_data.group_num);207seq_printf(m, "system_active_processors=%d\n",208ppp_data.active_system_procs);209210/* pool related entries are appropriate for shared configs */211if (lppaca_shared_proc()) {212unsigned long pool_idle_time, pool_procs;213214seq_printf(m, "pool=%d\n", ppp_data.pool_num);215216/* report pool_capacity in percentage */217seq_printf(m, "pool_capacity=%d\n",218ppp_data.active_procs_in_pool * 100);219220/* In case h_pic call is not successful, this would result in221* APP values being wrong in tools like lparstat.222*/223224if (h_pic(&pool_idle_time, &pool_procs) == H_SUCCESS) {225seq_printf(m, "pool_idle_time=%ld\n", pool_idle_time);226seq_printf(m, "pool_num_procs=%ld\n", pool_procs);227seq_printf(m, "boot_pool_idle_time=%ld\n", boot_pool_idle_time);228}229}230231seq_printf(m, "unallocated_capacity_weight=%d\n",232ppp_data.unallocated_weight);233seq_printf(m, "capacity_weight=%d\n", ppp_data.weight);234seq_printf(m, "capped=%d\n", ppp_data.capped);235seq_printf(m, "unallocated_capacity=%lld\n",236ppp_data.unallocated_entitlement);237238/* The last bits of information returned from h_get_ppp are only239* valid if the ibm,partition-performance-parameters-level240* property is >= 1.241*/242root = of_find_node_by_path("/");243if (root) {244perf_level = of_get_property(root,245"ibm,partition-performance-parameters-level",246NULL);247if (perf_level && (be32_to_cpup(perf_level) >= 1)) {248seq_printf(m,249"physical_procs_allocated_to_virtualization=%d\n",250ppp_data.phys_platform_procs);251seq_printf(m, "max_proc_capacity_available=%d\n",252ppp_data.max_proc_cap_avail);253seq_printf(m, "entitled_proc_capacity_available=%d\n",254ppp_data.entitled_proc_cap_avail);255}256257of_node_put(root);258}259}260261/**262* parse_mpp_data263* Parse out data returned from h_get_mpp264*/265static void parse_mpp_data(struct seq_file *m)266{267struct hvcall_mpp_data mpp_data;268int rc;269270rc = h_get_mpp(&mpp_data);271if (rc)272return;273274seq_printf(m, "entitled_memory=%ld\n", mpp_data.entitled_mem);275276if (mpp_data.mapped_mem != -1)277seq_printf(m, "mapped_entitled_memory=%ld\n",278mpp_data.mapped_mem);279280seq_printf(m, "entitled_memory_group_number=%d\n", mpp_data.group_num);281seq_printf(m, "entitled_memory_pool_number=%d\n", mpp_data.pool_num);282283seq_printf(m, "entitled_memory_weight=%d\n", mpp_data.mem_weight);284seq_printf(m, "unallocated_entitled_memory_weight=%d\n",285mpp_data.unallocated_mem_weight);286seq_printf(m, "unallocated_io_mapping_entitlement=%ld\n",287mpp_data.unallocated_entitlement);288289if (mpp_data.pool_size != -1)290seq_printf(m, "entitled_memory_pool_size=%ld bytes\n",291mpp_data.pool_size);292293seq_printf(m, "entitled_memory_loan_request=%ld\n",294mpp_data.loan_request);295296seq_printf(m, "backing_memory=%ld bytes\n", mpp_data.backing_mem);297}298299/**300* parse_mpp_x_data301* Parse out data returned from h_get_mpp_x302*/303static void parse_mpp_x_data(struct seq_file *m)304{305struct hvcall_mpp_x_data mpp_x_data;306307if (!firmware_has_feature(FW_FEATURE_XCMO))308return;309if (h_get_mpp_x(&mpp_x_data))310return;311312seq_printf(m, "coalesced_bytes=%ld\n", mpp_x_data.coalesced_bytes);313314if (mpp_x_data.pool_coalesced_bytes)315seq_printf(m, "pool_coalesced_bytes=%ld\n",316mpp_x_data.pool_coalesced_bytes);317if (mpp_x_data.pool_purr_cycles)318seq_printf(m, "coalesce_pool_purr=%ld\n", mpp_x_data.pool_purr_cycles);319if (mpp_x_data.pool_spurr_cycles)320seq_printf(m, "coalesce_pool_spurr=%ld\n", mpp_x_data.pool_spurr_cycles);321}322323/*324* Read the lpar name using the RTAS ibm,get-system-parameter call.325*326* The name read through this call is updated if changes are made by the end327* user on the hypervisor side.328*329* Some hypervisor (like Qemu) may not provide this value. In that case, a non330* null value is returned.331*/332static int read_rtas_lpar_name(struct seq_file *m)333{334struct papr_sysparm_buf *buf;335int err;336337buf = papr_sysparm_buf_alloc();338if (!buf)339return -ENOMEM;340341err = papr_sysparm_get(PAPR_SYSPARM_LPAR_NAME, buf);342if (!err)343seq_printf(m, "partition_name=%s\n", buf->val);344345papr_sysparm_buf_free(buf);346return err;347}348349/*350* Read the LPAR name from the Device Tree.351*352* The value read in the DT is not updated if the end-user is touching the LPAR353* name on the hypervisor side.354*/355static int read_dt_lpar_name(struct seq_file *m)356{357struct device_node *root = of_find_node_by_path("/");358const char *name;359int ret;360361ret = of_property_read_string(root, "ibm,partition-name", &name);362of_node_put(root);363if (ret)364return -ENOENT;365366seq_printf(m, "partition_name=%s\n", name);367return 0;368}369370static void read_lpar_name(struct seq_file *m)371{372if (read_rtas_lpar_name(m))373read_dt_lpar_name(m);374}375376#define SPLPAR_MAXLENGTH 1026*(sizeof(char))377378/*379* parse_system_parameter_string()380* Retrieve the potential_processors, max_entitled_capacity and friends381* through the get-system-parameter rtas call. Replace keyword strings as382* necessary.383*/384static void parse_system_parameter_string(struct seq_file *m)385{386struct papr_sysparm_buf *buf;387388buf = papr_sysparm_buf_alloc();389if (!buf)390return;391392if (papr_sysparm_get(PAPR_SYSPARM_SHARED_PROC_LPAR_ATTRS, buf)) {393goto out_free;394} else {395const char *local_buffer;396int splpar_strlen;397int idx, w_idx;398char *workbuffer = kzalloc(SPLPAR_MAXLENGTH, GFP_KERNEL);399400if (!workbuffer)401goto out_free;402403splpar_strlen = be16_to_cpu(buf->len);404local_buffer = buf->val;405406w_idx = 0;407idx = 0;408while ((*local_buffer) && (idx < splpar_strlen)) {409workbuffer[w_idx++] = local_buffer[idx++];410if ((local_buffer[idx] == ',')411|| (local_buffer[idx] == '\0')) {412workbuffer[w_idx] = '\0';413if (w_idx) {414/* avoid the empty string */415seq_printf(m, "%s\n", workbuffer);416}417memset(workbuffer, 0, SPLPAR_MAXLENGTH);418idx++; /* skip the comma */419w_idx = 0;420} else if (local_buffer[idx] == '=') {421/* code here to replace workbuffer contents422with different keyword strings */423if (0 == strcmp(workbuffer, "MaxEntCap")) {424strcpy(workbuffer,425"partition_max_entitled_capacity");426w_idx = strlen(workbuffer);427}428if (0 == strcmp(workbuffer, "MaxPlatProcs")) {429strcpy(workbuffer,430"system_potential_processors");431w_idx = strlen(workbuffer);432}433}434}435kfree(workbuffer);436local_buffer -= 2; /* back up over strlen value */437}438out_free:439papr_sysparm_buf_free(buf);440}441442/* Return the number of processors in the system.443* This function reads through the device tree and counts444* the virtual processors, this does not include threads.445*/446static int lparcfg_count_active_processors(void)447{448struct device_node *cpus_dn;449int count = 0;450451for_each_node_by_type(cpus_dn, "cpu") {452#ifdef LPARCFG_DEBUG453printk(KERN_ERR "cpus_dn %p\n", cpus_dn);454#endif455count++;456}457return count;458}459460static void pseries_cmo_data(struct seq_file *m)461{462int cpu;463unsigned long cmo_faults = 0;464unsigned long cmo_fault_time = 0;465466seq_printf(m, "cmo_enabled=%d\n", firmware_has_feature(FW_FEATURE_CMO));467468if (!firmware_has_feature(FW_FEATURE_CMO))469return;470471for_each_possible_cpu(cpu) {472cmo_faults += be64_to_cpu(lppaca_of(cpu).cmo_faults);473cmo_fault_time += be64_to_cpu(lppaca_of(cpu).cmo_fault_time);474}475476seq_printf(m, "cmo_faults=%lu\n", cmo_faults);477seq_printf(m, "cmo_fault_time_usec=%lu\n",478cmo_fault_time / tb_ticks_per_usec);479seq_printf(m, "cmo_primary_psp=%d\n", cmo_get_primary_psp());480seq_printf(m, "cmo_secondary_psp=%d\n", cmo_get_secondary_psp());481seq_printf(m, "cmo_page_size=%lu\n", cmo_get_page_size());482}483484static void splpar_dispatch_data(struct seq_file *m)485{486int cpu;487unsigned long dispatches = 0;488unsigned long dispatch_dispersions = 0;489490for_each_possible_cpu(cpu) {491dispatches += be32_to_cpu(lppaca_of(cpu).yield_count);492dispatch_dispersions +=493be32_to_cpu(lppaca_of(cpu).dispersion_count);494}495496seq_printf(m, "dispatches=%lu\n", dispatches);497seq_printf(m, "dispatch_dispersions=%lu\n", dispatch_dispersions);498}499500static void parse_em_data(struct seq_file *m)501{502unsigned long retbuf[PLPAR_HCALL_BUFSIZE];503504if (firmware_has_feature(FW_FEATURE_LPAR) &&505plpar_hcall(H_GET_EM_PARMS, retbuf) == H_SUCCESS)506seq_printf(m, "power_mode_data=%016lx\n", retbuf[0]);507}508509static void maxmem_data(struct seq_file *m)510{511unsigned long maxmem = 0;512513maxmem += (unsigned long)drmem_info->n_lmbs * drmem_info->lmb_size;514maxmem += hugetlb_total_pages() * PAGE_SIZE;515516seq_printf(m, "MaxMem=%lu\n", maxmem);517}518519static int pseries_lparcfg_data(struct seq_file *m, void *v)520{521int partition_potential_processors;522int partition_active_processors;523struct device_node *rtas_node;524const __be32 *lrdrp = NULL;525526rtas_node = of_find_node_by_path("/rtas");527if (rtas_node)528lrdrp = of_get_property(rtas_node, "ibm,lrdr-capacity", NULL);529530if (lrdrp == NULL) {531partition_potential_processors = num_possible_cpus();532} else {533partition_potential_processors = be32_to_cpup(lrdrp + 4);534}535of_node_put(rtas_node);536537partition_active_processors = lparcfg_count_active_processors();538539if (firmware_has_feature(FW_FEATURE_SPLPAR)) {540/* this call handles the ibm,get-system-parameter contents */541read_lpar_name(m);542parse_system_parameter_string(m);543parse_ppp_data(m);544parse_mpp_data(m);545parse_mpp_x_data(m);546pseries_cmo_data(m);547splpar_dispatch_data(m);548549seq_printf(m, "purr=%ld\n", get_purr());550seq_printf(m, "tbr=%ld\n", mftb());551} else { /* non SPLPAR case */552553seq_printf(m, "system_active_processors=%d\n",554partition_active_processors);555556seq_printf(m, "system_potential_processors=%d\n",557partition_potential_processors);558559seq_printf(m, "partition_max_entitled_capacity=%d\n",560partition_potential_processors * 100);561562seq_printf(m, "partition_entitled_capacity=%d\n",563partition_active_processors * 100);564}565566show_gpci_data(m);567568seq_printf(m, "partition_active_processors=%d\n",569partition_active_processors);570571seq_printf(m, "partition_potential_processors=%d\n",572partition_potential_processors);573574seq_printf(m, "shared_processor_mode=%d\n",575lppaca_shared_proc());576577#ifdef CONFIG_PPC_64S_HASH_MMU578if (!radix_enabled())579seq_printf(m, "slb_size=%d\n", mmu_slb_size);580#endif581parse_em_data(m);582maxmem_data(m);583584seq_printf(m, "security_flavor=%u\n", pseries_security_flavor);585586return 0;587}588589static ssize_t update_ppp(u64 *entitlement, u8 *weight)590{591struct hvcall_ppp_data ppp_data;592u8 new_weight;593u64 new_entitled;594ssize_t retval;595596/* Get our current parameters */597retval = h_get_ppp(&ppp_data);598if (retval)599return retval;600601if (entitlement) {602new_weight = ppp_data.weight;603new_entitled = *entitlement;604} else if (weight) {605new_weight = *weight;606new_entitled = ppp_data.entitlement;607} else608return -EINVAL;609610pr_debug("%s: current_entitled = %llu, current_weight = %u\n",611__func__, ppp_data.entitlement, ppp_data.weight);612613pr_debug("%s: new_entitled = %llu, new_weight = %u\n",614__func__, new_entitled, new_weight);615616retval = plpar_hcall_norets(H_SET_PPP, new_entitled, new_weight);617return retval;618}619620/**621* update_mpp622*623* Update the memory entitlement and weight for the partition. Caller must624* specify either a new entitlement or weight, not both, to be updated625* since the h_set_mpp call takes both entitlement and weight as parameters.626*/627static ssize_t update_mpp(u64 *entitlement, u8 *weight)628{629struct hvcall_mpp_data mpp_data;630u64 new_entitled;631u8 new_weight;632ssize_t rc;633634if (entitlement) {635/* Check with vio to ensure the new memory entitlement636* can be handled.637*/638rc = vio_cmo_entitlement_update(*entitlement);639if (rc)640return rc;641}642643rc = h_get_mpp(&mpp_data);644if (rc)645return rc;646647if (entitlement) {648new_weight = mpp_data.mem_weight;649new_entitled = *entitlement;650} else if (weight) {651new_weight = *weight;652new_entitled = mpp_data.entitled_mem;653} else654return -EINVAL;655656pr_debug("%s: current_entitled = %lu, current_weight = %u\n",657__func__, mpp_data.entitled_mem, mpp_data.mem_weight);658659pr_debug("%s: new_entitled = %llu, new_weight = %u\n",660__func__, new_entitled, new_weight);661662rc = plpar_hcall_norets(H_SET_MPP, new_entitled, new_weight);663return rc;664}665666/*667* Interface for changing system parameters (variable capacity weight668* and entitled capacity). Format of input is "param_name=value";669* anything after value is ignored. Valid parameters at this time are670* "partition_entitled_capacity" and "capacity_weight". We use671* H_SET_PPP to alter parameters.672*673* This function should be invoked only on systems with674* FW_FEATURE_SPLPAR.675*/676static ssize_t lparcfg_write(struct file *file, const char __user * buf,677size_t count, loff_t * off)678{679char kbuf[64];680char *tmp;681u64 new_entitled, *new_entitled_ptr = &new_entitled;682u8 new_weight, *new_weight_ptr = &new_weight;683ssize_t retval;684685if (!firmware_has_feature(FW_FEATURE_SPLPAR))686return -EINVAL;687688if (count > sizeof(kbuf))689return -EINVAL;690691if (copy_from_user(kbuf, buf, count))692return -EFAULT;693694kbuf[count - 1] = '\0';695tmp = strchr(kbuf, '=');696if (!tmp)697return -EINVAL;698699*tmp++ = '\0';700701if (!strcmp(kbuf, "partition_entitled_capacity")) {702char *endp;703*new_entitled_ptr = (u64) simple_strtoul(tmp, &endp, 10);704if (endp == tmp)705return -EINVAL;706707retval = update_ppp(new_entitled_ptr, NULL);708709if (retval == H_SUCCESS || retval == H_CONSTRAINED) {710/*711* The hypervisor assigns VAS resources based712* on entitled capacity for shared mode.713* Reconfig VAS windows based on DLPAR CPU events.714*/715if (pseries_vas_dlpar_cpu() != 0)716retval = H_HARDWARE;717}718} else if (!strcmp(kbuf, "capacity_weight")) {719char *endp;720*new_weight_ptr = (u8) simple_strtoul(tmp, &endp, 10);721if (endp == tmp)722return -EINVAL;723724retval = update_ppp(NULL, new_weight_ptr);725} else if (!strcmp(kbuf, "entitled_memory")) {726char *endp;727*new_entitled_ptr = (u64) simple_strtoul(tmp, &endp, 10);728if (endp == tmp)729return -EINVAL;730731retval = update_mpp(new_entitled_ptr, NULL);732} else if (!strcmp(kbuf, "entitled_memory_weight")) {733char *endp;734*new_weight_ptr = (u8) simple_strtoul(tmp, &endp, 10);735if (endp == tmp)736return -EINVAL;737738retval = update_mpp(NULL, new_weight_ptr);739} else740return -EINVAL;741742if (retval == H_SUCCESS || retval == H_CONSTRAINED) {743retval = count;744} else if (retval == H_BUSY) {745retval = -EBUSY;746} else if (retval == H_HARDWARE) {747retval = -EIO;748} else if (retval == H_PARAMETER) {749retval = -EINVAL;750}751752return retval;753}754755static int lparcfg_data(struct seq_file *m, void *v)756{757struct device_node *rootdn;758const char *model = "";759const char *system_id = "";760const char *tmp;761const __be32 *lp_index_ptr;762unsigned int lp_index = 0;763764seq_printf(m, "%s %s\n", MODULE_NAME, MODULE_VERS);765766rootdn = of_find_node_by_path("/");767if (rootdn) {768tmp = of_get_property(rootdn, "model", NULL);769if (tmp)770model = tmp;771tmp = of_get_property(rootdn, "system-id", NULL);772if (tmp)773system_id = tmp;774lp_index_ptr = of_get_property(rootdn, "ibm,partition-no",775NULL);776if (lp_index_ptr)777lp_index = be32_to_cpup(lp_index_ptr);778of_node_put(rootdn);779}780seq_printf(m, "serial_number=%s\n", system_id);781seq_printf(m, "system_type=%s\n", model);782seq_printf(m, "partition_id=%d\n", (int)lp_index);783784return pseries_lparcfg_data(m, v);785}786787static int lparcfg_open(struct inode *inode, struct file *file)788{789return single_open(file, lparcfg_data, NULL);790}791792static const struct proc_ops lparcfg_proc_ops = {793.proc_read = seq_read,794.proc_write = lparcfg_write,795.proc_open = lparcfg_open,796.proc_release = single_release,797.proc_lseek = seq_lseek,798};799800static int __init lparcfg_init(void)801{802umode_t mode = 0444;803long retval;804805/* Allow writing if we have FW_FEATURE_SPLPAR */806if (firmware_has_feature(FW_FEATURE_SPLPAR))807mode |= 0200;808809if (!proc_create("powerpc/lparcfg", mode, NULL, &lparcfg_proc_ops)) {810printk(KERN_ERR "Failed to create powerpc/lparcfg\n");811return -EIO;812}813814/* If this call fails, it would result in APP values815* being wrong for since boot reports of lparstat816*/817retval = h_pic(&boot_pool_idle_time, NULL);818819if (retval != H_SUCCESS)820pr_debug("H_PIC failed during lparcfg init retval: %ld\n",821retval);822823return 0;824}825machine_device_initcall(pseries, lparcfg_init);826827828