/* * PowerPC64 LPAR Configuration Information Driver * * Dave Engebretsen engebret@us.ibm.com * Copyright (c) 2003 Dave Engebretsen * Will Schmidt willschm@us.ibm.com * SPLPAR updates, Copyright (c) 2003 Will Schmidt IBM Corporation. * Nathan Lynch nathanl@austin.ibm.com * Added lparcfg_write, Copyright (C) 2004 Nathan Lynch IBM Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * This driver creates a proc file at /proc/ppc64/lparcfg which contains * keyword - value pairs that specify the configuration of the partition. */ #include #include #include #include #include #include #include #include #include #include #include #define MODULE_VERS "1.0" #define MODULE_NAME "lparcfg" static struct proc_dir_entry *proc_ppc64_lparcfg; #define LPARCFG_BUFF_SIZE 4096 #ifdef CONFIG_PPC_ISERIES #define lparcfg_write NULL static unsigned char e2a(unsigned char x) { switch (x) { case 0xF0: return '0'; case 0xF1: return '1'; case 0xF2: return '2'; case 0xF3: return '3'; case 0xF4: return '4'; case 0xF5: return '5'; case 0xF6: return '6'; case 0xF7: return '7'; case 0xF8: return '8'; case 0xF9: return '9'; case 0xC1: return 'A'; case 0xC2: return 'B'; case 0xC3: return 'C'; case 0xC4: return 'D'; case 0xC5: return 'E'; case 0xC6: return 'F'; case 0xC7: return 'G'; case 0xC8: return 'H'; case 0xC9: return 'I'; case 0xD1: return 'J'; case 0xD2: return 'K'; case 0xD3: return 'L'; case 0xD4: return 'M'; case 0xD5: return 'N'; case 0xD6: return 'O'; case 0xD7: return 'P'; case 0xD8: return 'Q'; case 0xD9: return 'R'; case 0xE2: return 'S'; case 0xE3: return 'T'; case 0xE4: return 'U'; case 0xE5: return 'V'; case 0xE6: return 'W'; case 0xE7: return 'X'; case 0xE8: return 'Y'; case 0xE9: return 'Z'; } return ' '; } /* * Methods used to fetch LPAR data when running on an iSeries platform. */ static int lparcfg_data(unsigned char *buf, unsigned long size) { unsigned long n = 0, pool_id, lp_index; int shared, entitled_capacity, max_entitled_capacity; int processors, max_processors; struct paca_struct *lpaca = get_paca(); if((buf == NULL) || (size > LPARCFG_BUFF_SIZE)) { return -EFAULT; } memset(buf, 0, size); shared = (int)(lpaca->xLpPacaPtr->xSharedProc); n += scnprintf(buf, LPARCFG_BUFF_SIZE - n, "serial_number=%c%c%c%c%c%c%c\n", e2a(xItExtVpdPanel.mfgID[2]), e2a(xItExtVpdPanel.mfgID[3]), e2a(xItExtVpdPanel.systemSerial[1]), e2a(xItExtVpdPanel.systemSerial[2]), e2a(xItExtVpdPanel.systemSerial[3]), e2a(xItExtVpdPanel.systemSerial[4]), e2a(xItExtVpdPanel.systemSerial[5])); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_type=%c%c%c%c\n", e2a(xItExtVpdPanel.machineType[0]), e2a(xItExtVpdPanel.machineType[1]), e2a(xItExtVpdPanel.machineType[2]), e2a(xItExtVpdPanel.machineType[3])); lp_index = HvLpConfig_getLpIndex(); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_id=%d\n", (int)lp_index); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_active_processors=%d\n", (int)HvLpConfig_getSystemPhysicalProcessors()); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_potential_processors=%d\n", (int)HvLpConfig_getSystemPhysicalProcessors()); processors = (int)HvLpConfig_getPhysicalProcessors(); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_active_processors=%d\n", processors); max_processors = (int)HvLpConfig_getMaxPhysicalProcessors(); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_potential_processors=%d\n", max_processors); if(shared) { entitled_capacity = HvLpConfig_getSharedProcUnits(); max_entitled_capacity = HvLpConfig_getMaxSharedProcUnits(); } else { entitled_capacity = processors * 100; max_entitled_capacity = max_processors * 100; } n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_entitled_capacity=%d\n", entitled_capacity); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_max_entitled_capacity=%d\n", max_entitled_capacity); if(shared) { pool_id = HvLpConfig_getSharedPoolIndex(); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "pool=%d\n", (int)pool_id); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "pool_capacity=%d\n", (int)(HvLpConfig_getNumProcsInSharedPool(pool_id)*100)); } n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "shared_processor_mode=%d\n", shared); return 0; } #endif /* CONFIG_PPC_ISERIES */ #ifdef CONFIG_PPC_PSERIES /* * Methods used to fetch LPAR data when running on a pSeries platform. */ /* * H_GET_PPP hcall returns info in 4 parms. * entitled_capacity,unallocated_capacity, * aggregation, resource_capability). * * R4 = Entitled Processor Capacity Percentage. * R5 = Unallocated Processor Capacity Percentage. * R6 (AABBCCDDEEFFGGHH). * XXXX - reserved (0) * XXXX - reserved (0) * XXXX - Group Number * XXXX - Pool Number. * R7 (PPOONNMMLLKKJJII) * XX - reserved. (0) * XX - bit 0-6 reserved (0). bit 7 is Capped indicator. * XX - variable processor Capacity Weight * XX - Unallocated Variable Processor Capacity Weight. * XXXX - Active processors in Physical Processor Pool. * XXXX - Processors active on platform. */ unsigned int h_get_ppp(unsigned long *entitled,unsigned long *unallocated,unsigned long *aggregation,unsigned long *resource) { unsigned long rc; rc = plpar_hcall_4out(H_GET_PPP,0,0,0,0,entitled,unallocated,aggregation,resource); return 0; } /* * get_splpar_potential_characteristics(). * Retrieve the potential_processors and max_entitled_capacity values * through the get-system-parameter rtas call. */ #define SPLPAR_CHARACTERISTICS_TOKEN 20 #define SPLPAR_MAXLENGTH 1026*(sizeof(char)) unsigned int get_splpar_potential_characteristics(void) { /* return 0 for now. Underlying rtas functionality is not yet complete. 12/01/2003*/ return 0; #if 0 long call_status; unsigned long ret[2]; char * buffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL); printk("token for ibm,get-system-parameter (0x%x)\n",rtas_token("ibm,get-system-parameter")); call_status = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, NULL, SPLPAR_CHARACTERISTICS_TOKEN, &buffer, SPLPAR_MAXLENGTH, (void *)&ret); if (call_status!=0) { printk("Error calling get-system-parameter (0x%lx)\n",call_status); kfree(buffer); return -1; } else { printk("get-system-parameter (%s)\n",buffer); kfree(buffer); /* TODO: Add code here to parse out value for system_potential_processors and partition_max_entitled_capacity */ return 1; } #endif } static int lparcfg_data(unsigned char *buf, unsigned long size) { unsigned long n = 0; int shared, max_entitled_capacity; int processors, system_active_processors, system_potential_processors; struct device_node *root; const char *model = ""; const char *system_id = ""; unsigned int *lp_index_ptr, lp_index = 0; struct device_node *rtas_node; int *ip; unsigned long h_entitled,h_unallocated,h_aggregation,h_resource; if((buf == NULL) || (size > LPARCFG_BUFF_SIZE)) { return -EFAULT; } memset(buf, 0, size); root = find_path_device("/"); if (root) { model = get_property(root, "model", NULL); system_id = get_property(root, "system-id", NULL); lp_index_ptr = (unsigned int *)get_property(root, "ibm,partition-no", NULL); if(lp_index_ptr) lp_index = *lp_index_ptr; } n = scnprintf(buf, LPARCFG_BUFF_SIZE - n, "serial_number=%s\n", system_id); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_type=%s\n", model); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_id=%d\n", (int)lp_index); rtas_node = find_path_device("/rtas"); ip = (int *)get_property(rtas_node, "ibm,lrdr-capacity", NULL); if (ip == NULL) { system_active_processors = systemcfg->processorCount; } else { system_active_processors = *(ip + 4); } if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { h_get_ppp(&h_entitled,&h_unallocated,&h_aggregation,&h_resource); #ifdef DEBUG n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "R4=0x%lx\n", h_entitled); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "R5=0x%lx\n", h_unallocated); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "R6=0x%lx\n", h_aggregation); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "R7=0x%lx\n", h_resource); #endif /* DEBUG */ } if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { system_potential_processors = get_splpar_potential_characteristics(); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_active_processors=%ld\n", (h_resource >> 2*8) & 0xffff); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_potential_processors=%d\n", system_potential_processors); } else { system_potential_processors = system_active_processors; n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_active_processors=%d\n", system_active_processors); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_potential_processors=%d\n", system_potential_processors); } processors = systemcfg->processorCount; n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_active_processors=%d\n", processors); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_potential_processors=%d\n", system_active_processors); /* max_entitled_capacity will come out of get_splpar_potential_characteristics() when that function is complete */ max_entitled_capacity = system_active_processors * 100; if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_entitled_capacity=%ld\n", h_entitled); } else { n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_entitled_capacity=%d\n", system_active_processors*100); } n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_max_entitled_capacity=%d\n", max_entitled_capacity); shared = 0; n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "shared_processor_mode=%d\n", shared); if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "pool=%ld\n", (h_aggregation >> 0*8)&0xffff); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "pool_capacity=%ld\n", (h_resource >> 3*8) &0xffff); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "group=%ld\n", (h_aggregation >> 2*8)&0xffff); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "capped=%ld\n", (h_resource >> 6*8)&0x40); n += scnprintf(buf+n, LPARCFG_BUFF_SIZE - n, "capacity_weight=%d\n", (int)(h_resource>>5*8)&0xFF); } return 0; } /* * Interface for changing system parameters (variable capacity weight * and entitled capacity). Format of input is "param_name=value"; * anything after value is ignored. Valid parameters at this time are * "partition_entitled_capacity" and "capacity_weight". We use * H_SET_PPP to alter parameters. * * This function should be invoked only on systems with * FW_FEATURE_SPLPAR. */ static ssize_t lparcfg_write(struct file *file, const char __user *buf, size_t count, loff_t *off) { char *kbuf; char *tmp; u64 new_entitled, *new_entitled_ptr = &new_entitled; u8 new_weight, *new_weight_ptr = &new_weight; unsigned long current_entitled; /* parameters for h_get_ppp */ unsigned long dummy; unsigned long resource; u8 current_weight; ssize_t retval = -ENOMEM; kbuf = kmalloc(count, GFP_KERNEL); if (!kbuf) goto out; retval = -EFAULT; if (copy_from_user(kbuf, buf, count)) goto out; retval = -EINVAL; kbuf[count - 1] = '\0'; tmp = strchr(kbuf, '='); if (!tmp) goto out; *tmp++ = '\0'; if (!strcmp(kbuf, "partition_entitled_capacity")) { char *endp; *new_entitled_ptr = (u64)simple_strtoul(tmp, &endp, 10); if (endp == tmp) goto out; new_weight_ptr = ¤t_weight; } else if (!strcmp(kbuf, "capacity_weight")) { char *endp; *new_weight_ptr = (u8)simple_strtoul(tmp, &endp, 10); if (endp == tmp) goto out; new_entitled_ptr = ¤t_entitled; } else goto out; /* Get our current parameters */ retval = h_get_ppp(¤t_entitled, &dummy, &dummy, &resource); if (retval) { retval = -EIO; goto out; } current_weight = (resource>>5*8)&0xFF; pr_debug("%s: current_entitled = %lu, current_weight = %lu\n", __FUNCTION__, current_entitled, current_weight); pr_debug("%s: new_entitled = %lu, new_weight = %lu\n", __FUNCTION__, *new_entitled_ptr, *new_weight_ptr); retval = plpar_hcall_norets(H_SET_PPP, *new_entitled_ptr, *new_weight_ptr); if (retval == H_Success || retval == H_Constrained) { retval = count; } else if (retval == H_Busy) { retval = -EBUSY; } else if (retval == H_Hardware) { retval = -EIO; } else if (retval == H_Parameter) { retval = -EINVAL; } else { printk(KERN_WARNING "%s: received unknown hv return code %ld", __FUNCTION__, retval); retval = -EIO; } out: kfree(kbuf); return retval; } #endif /* CONFIG_PPC_PSERIES */ static ssize_t lparcfg_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode); unsigned long *data = (unsigned long *)dp->data; unsigned long p; ssize_t read; char * pnt; if (!data) { printk(KERN_ERR "lparcfg: read failed no data\n"); return -EIO; } if(ppos) { p = *ppos; } else { return -EFAULT; } if (p >= LPARCFG_BUFF_SIZE) return 0; lparcfg_data((unsigned char *)data, LPARCFG_BUFF_SIZE); if (count > (strlen((char *)data) - p)) count = (strlen((char *)data)) - p; read = 0; pnt = (char *)(data) + p; copy_to_user(buf, (void *)pnt, count); read += count; *ppos += read; return read; } static int lparcfg_open(struct inode * inode, struct file * file) { struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode); unsigned int *data = (unsigned int *)dp->data; if (!data) { printk(KERN_ERR "lparcfg: open failed no data\n"); return -EIO; } return 0; } struct file_operations lparcfg_fops = { owner: THIS_MODULE, read: lparcfg_read, open: lparcfg_open, }; int __init lparcfg_init(void) { struct proc_dir_entry *ent; mode_t mode = S_IRUSR; /* Allow writing if we have FW_FEATURE_SPLPAR */ if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { lparcfg_fops.write = lparcfg_write; mode |= S_IWUSR; } ent = create_proc_entry("ppc64/lparcfg", mode, NULL); if (ent) { ent->proc_fops = &lparcfg_fops; ent->data = kmalloc(LPARCFG_BUFF_SIZE, GFP_KERNEL); if (!ent->data) { printk(KERN_ERR "Failed to allocate buffer for lparcfg\n"); remove_proc_entry("lparcfg", ent->parent); return -ENOMEM; } } else { printk(KERN_ERR "Failed to create ppc64/lparcfg\n"); return -EIO; } proc_ppc64_lparcfg = ent; return 0; } void __exit lparcfg_cleanup(void) { if (proc_ppc64_lparcfg) { if (proc_ppc64_lparcfg->data) { kfree(proc_ppc64_lparcfg->data); } remove_proc_entry("lparcfg", proc_ppc64_lparcfg->parent); } } module_init(lparcfg_init); module_exit(lparcfg_cleanup); MODULE_DESCRIPTION("Interface for LPAR configuration data"); MODULE_AUTHOR("Dave Engebretsen"); MODULE_LICENSE("GPL");