From: Anton Blanchard <anton@samba.org>

Add lparcfg_write() for changing SPLPAR system parameters



---

 arch/ppc64/kernel/lparcfg.c |  107 +++++++++++++++++++++++++++++++++++++++++++-
 include/asm-ppc64/hvcall.h  |    1 
 2 files changed, 107 insertions(+), 1 deletion(-)

diff -puN arch/ppc64/kernel/lparcfg.c~ppc64-lparcfg_write arch/ppc64/kernel/lparcfg.c
--- 25/arch/ppc64/kernel/lparcfg.c~ppc64-lparcfg_write	2004-01-21 10:43:00.000000000 -0800
+++ 25-akpm/arch/ppc64/kernel/lparcfg.c	2004-01-21 10:43:00.000000000 -0800
@@ -5,6 +5,8 @@
  *    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
@@ -33,6 +35,9 @@ static struct proc_dir_entry *proc_ppc64
 #define LPARCFG_BUFF_SIZE 4096
 
 #ifdef CONFIG_PPC_ISERIES
+
+#define lparcfg_write NULL
+
 static unsigned char e2a(unsigned char x)
 {
         switch (x) {
@@ -383,6 +388,99 @@ static int lparcfg_data(unsigned char *b
 	}
 	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 = &current_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 = &current_entitled;
+	} else
+		goto out;
+
+	/* Get our current parameters */
+	retval = h_get_ppp(&current_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 */
 
 
@@ -442,8 +540,15 @@ struct file_operations lparcfg_fops = {
 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", S_IRUSR, NULL);
+	ent = create_proc_entry("ppc64/lparcfg", mode, NULL);
 	if (ent) {
 		ent->proc_fops = &lparcfg_fops;
 		ent->data = kmalloc(LPARCFG_BUFF_SIZE, GFP_KERNEL);
diff -puN include/asm-ppc64/hvcall.h~ppc64-lparcfg_write include/asm-ppc64/hvcall.h
--- 25/include/asm-ppc64/hvcall.h~ppc64-lparcfg_write	2004-01-21 10:43:00.000000000 -0800
+++ 25-akpm/include/asm-ppc64/hvcall.h	2004-01-21 10:43:00.000000000 -0800
@@ -3,6 +3,7 @@
 
 #define H_Success	0
 #define H_Busy		1	/* Hardware busy -- retry later */
+#define H_Constrained	4	/* Resource request constrained to max allowed */
 #define H_Hardware	-1	/* Hardware error */
 #define H_Function	-2	/* Function not supported */
 #define H_Privilege	-3	/* Caller not privileged */

_