blob: a0ca90ab5e3975b12a7fdd8ad49fea4ea0919279 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * PowerPC64 LPAR Configuration Information Driver
3 *
4 * Dave Engebretsen engebret@us.ibm.com
5 * Copyright (c) 2003 Dave Engebretsen
6 * Will Schmidt willschm@us.ibm.com
7 * SPLPAR updates, Copyright (c) 2003 Will Schmidt IBM Corporation.
8 * seq_file updates, Copyright (c) 2004 Will Schmidt IBM Corporation.
9 * Nathan Lynch nathanl@austin.ibm.com
10 * 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 *
17 * This driver creates a proc file at /proc/ppc64/lparcfg which contains
18 * keyword - value pairs that specify the configuration of the partition.
19 */
20
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/module.h>
22#include <linux/types.h>
23#include <linux/errno.h>
24#include <linux/proc_fs.h>
25#include <linux/init.h>
26#include <linux/seq_file.h>
27#include <asm/uaccess.h>
Kelly Daly15b17182005-11-02 11:55:28 +110028#include <asm/iseries/hv_lp_config.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <asm/lppaca.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <asm/hvcall.h>
Stephen Rothwell1ababe12005-08-03 14:35:25 +100031#include <asm/firmware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <asm/rtas.h>
33#include <asm/system.h>
34#include <asm/time.h>
Michael Ellerman856509d2005-06-25 14:54:42 -070035#include <asm/prom.h>
Paul Mackerras271c3f32005-11-11 23:04:40 +110036#include <asm/vdso_datapage.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Will Schmidt34422fe2006-03-31 09:07:48 -060038#define MODULE_VERS "1.7"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#define MODULE_NAME "lparcfg"
40
41/* #define LPARCFG_DEBUG */
42
Linus Torvalds1da177e2005-04-16 15:20:36 -070043static struct proc_dir_entry *proc_ppc64_lparcfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Linus Torvalds1da177e2005-04-16 15:20:36 -070045/*
Stephen Rothwell16e9f992006-06-29 15:07:42 +100046 * Track sum of all purrs across all processors. This is used to further
47 * calculate usage values by different applications
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 */
49static unsigned long get_purr(void)
50{
51 unsigned long sum_purr = 0;
52 int cpu;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
KAMEZAWA Hiroyuki0e551952006-03-28 14:50:51 -080054 for_each_possible_cpu(cpu) {
Stephen Rothwell16e9f992006-06-29 15:07:42 +100055 if (firmware_has_feature(FW_FEATURE_ISERIES))
56 sum_purr += lppaca[cpu].emulated_time_base;
57 else {
58 struct cpu_usage *cu;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Stephen Rothwell16e9f992006-06-29 15:07:42 +100060 cu = &per_cpu(cpu_usage_array, cpu);
61 sum_purr += cu->current_tb;
62 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 }
64 return sum_purr;
65}
66
Stephen Rothwell16e9f992006-06-29 15:07:42 +100067#ifdef CONFIG_PPC_ISERIES
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
David Gibsond3d21762005-11-10 15:26:20 +110069/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 * Methods used to fetch LPAR data when running on an iSeries platform.
71 */
Stephen Rothwell16e9f992006-06-29 15:07:42 +100072static int iseries_lparcfg_data(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -070073{
Stephen Rothwell16e9f992006-06-29 15:07:42 +100074 unsigned long pool_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 int shared, entitled_capacity, max_entitled_capacity;
76 int processors, max_processors;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 unsigned long purr = get_purr();
78
Hugh Dickins048c8bc2006-11-01 05:44:54 +110079 shared = (int)(local_paca->lppaca_ptr->shared_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81 seq_printf(m, "system_active_processors=%d\n",
82 (int)HvLpConfig_getSystemPhysicalProcessors());
83
84 seq_printf(m, "system_potential_processors=%d\n",
85 (int)HvLpConfig_getSystemPhysicalProcessors());
86
87 processors = (int)HvLpConfig_getPhysicalProcessors();
88 seq_printf(m, "partition_active_processors=%d\n", processors);
89
90 max_processors = (int)HvLpConfig_getMaxPhysicalProcessors();
91 seq_printf(m, "partition_potential_processors=%d\n", max_processors);
92
93 if (shared) {
94 entitled_capacity = HvLpConfig_getSharedProcUnits();
95 max_entitled_capacity = HvLpConfig_getMaxSharedProcUnits();
96 } else {
97 entitled_capacity = processors * 100;
98 max_entitled_capacity = max_processors * 100;
99 }
100 seq_printf(m, "partition_entitled_capacity=%d\n", entitled_capacity);
101
102 seq_printf(m, "partition_max_entitled_capacity=%d\n",
103 max_entitled_capacity);
104
105 if (shared) {
106 pool_id = HvLpConfig_getSharedPoolIndex();
107 seq_printf(m, "pool=%d\n", (int)pool_id);
108 seq_printf(m, "pool_capacity=%d\n",
109 (int)(HvLpConfig_getNumProcsInSharedPool(pool_id) *
110 100));
111 seq_printf(m, "purr=%ld\n", purr);
112 }
113
114 seq_printf(m, "shared_processor_mode=%d\n", shared);
115
116 return 0;
117}
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000118
119#else /* CONFIG_PPC_ISERIES */
120
121static int iseries_lparcfg_data(struct seq_file *m, void *v)
122{
123 return 0;
124}
125
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126#endif /* CONFIG_PPC_ISERIES */
127
128#ifdef CONFIG_PPC_PSERIES
David Gibsond3d21762005-11-10 15:26:20 +1100129/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 * Methods used to fetch LPAR data when running on a pSeries platform.
131 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132/*
133 * H_GET_PPP hcall returns info in 4 parms.
134 * entitled_capacity,unallocated_capacity,
135 * aggregation, resource_capability).
136 *
David Gibsond3d21762005-11-10 15:26:20 +1100137 * R4 = Entitled Processor Capacity Percentage.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 * R5 = Unallocated Processor Capacity Percentage.
139 * R6 (AABBCCDDEEFFGGHH).
140 * XXXX - reserved (0)
141 * XXXX - reserved (0)
142 * XXXX - Group Number
143 * XXXX - Pool Number.
144 * R7 (IIJJKKLLMMNNOOPP).
145 * XX - reserved. (0)
146 * XX - bit 0-6 reserved (0). bit 7 is Capped indicator.
147 * XX - variable processor Capacity Weight
148 * XX - Unallocated Variable Processor Capacity Weight.
149 * XXXX - Active processors in Physical Processor Pool.
David Gibsond3d21762005-11-10 15:26:20 +1100150 * XXXX - Processors active on platform.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 */
152static unsigned int h_get_ppp(unsigned long *entitled,
153 unsigned long *unallocated,
154 unsigned long *aggregation,
155 unsigned long *resource)
156{
157 unsigned long rc;
Anton Blanchardb9377ff2006-07-19 08:01:28 +1000158 unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
159
160 rc = plpar_hcall(H_GET_PPP, retbuf);
161
162 *entitled = retbuf[0];
163 *unallocated = retbuf[1];
164 *aggregation = retbuf[2];
165 *resource = retbuf[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 return rc;
168}
169
Nathan Fotenot11529392008-07-24 04:25:16 +1000170static unsigned h_pic(unsigned long *pool_idle_time,
171 unsigned long *num_procs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172{
173 unsigned long rc;
Anton Blanchardb9377ff2006-07-19 08:01:28 +1000174 unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
175
176 rc = plpar_hcall(H_PIC, retbuf);
177
178 *pool_idle_time = retbuf[0];
179 *num_procs = retbuf[1];
Nathan Fotenot11529392008-07-24 04:25:16 +1000180
181 return rc;
182}
183
184/*
185 * parse_ppp_data
186 * Parse out the data returned from h_get_ppp and h_pic
187 */
188static void parse_ppp_data(struct seq_file *m)
189{
190 unsigned long h_entitled, h_unallocated;
191 unsigned long h_aggregation, h_resource;
192 int rc;
193
194 rc = h_get_ppp(&h_entitled, &h_unallocated, &h_aggregation,
195 &h_resource);
196 if (rc)
197 return;
198
199 seq_printf(m, "partition_entitled_capacity=%ld\n", h_entitled);
200 seq_printf(m, "group=%ld\n", (h_aggregation >> 2 * 8) & 0xffff);
201 seq_printf(m, "system_active_processors=%ld\n",
202 (h_resource >> 0 * 8) & 0xffff);
203
204 /* pool related entries are apropriate for shared configs */
205 if (lppaca[0].shared_proc) {
206 unsigned long pool_idle_time, pool_procs;
207
208 seq_printf(m, "pool=%ld\n", (h_aggregation >> 0 * 8) & 0xffff);
209
210 /* report pool_capacity in percentage */
211 seq_printf(m, "pool_capacity=%ld\n",
212 ((h_resource >> 2 * 8) & 0xffff) * 100);
213
214 h_pic(&pool_idle_time, &pool_procs);
215 seq_printf(m, "pool_idle_time=%ld\n", pool_idle_time);
216 seq_printf(m, "pool_num_procs=%ld\n", pool_procs);
217 }
218
219 seq_printf(m, "unallocated_capacity_weight=%ld\n",
220 (h_resource >> 4 * 8) & 0xFF);
221
222 seq_printf(m, "capacity_weight=%ld\n", (h_resource >> 5 * 8) & 0xFF);
223 seq_printf(m, "capped=%ld\n", (h_resource >> 6 * 8) & 0x01);
224 seq_printf(m, "unallocated_capacity=%ld\n", h_unallocated);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225}
226
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227#define SPLPAR_CHARACTERISTICS_TOKEN 20
228#define SPLPAR_MAXLENGTH 1026*(sizeof(char))
229
230/*
231 * parse_system_parameter_string()
232 * Retrieve the potential_processors, max_entitled_capacity and friends
233 * through the get-system-parameter rtas call. Replace keyword strings as
234 * necessary.
235 */
236static void parse_system_parameter_string(struct seq_file *m)
237{
238 int call_status;
239
Will Schmidt34422fe2006-03-31 09:07:48 -0600240 unsigned char *local_buffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 if (!local_buffer) {
242 printk(KERN_ERR "%s %s kmalloc failure at line %d \n",
Harvey Harrisone48b1b42008-03-29 08:21:07 +1100243 __FILE__, __func__, __LINE__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 return;
245 }
246
247 spin_lock(&rtas_data_buf_lock);
248 memset(rtas_data_buf, 0, SPLPAR_MAXLENGTH);
249 call_status = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1,
250 NULL,
251 SPLPAR_CHARACTERISTICS_TOKEN,
Will Schmidt34422fe2006-03-31 09:07:48 -0600252 __pa(rtas_data_buf),
253 RTAS_DATA_BUF_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 memcpy(local_buffer, rtas_data_buf, SPLPAR_MAXLENGTH);
255 spin_unlock(&rtas_data_buf_lock);
256
257 if (call_status != 0) {
258 printk(KERN_INFO
259 "%s %s Error calling get-system-parameter (0x%x)\n",
Harvey Harrisone48b1b42008-03-29 08:21:07 +1100260 __FILE__, __func__, call_status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 } else {
262 int splpar_strlen;
263 int idx, w_idx;
Yoann Padioleaudd00cc42007-07-19 01:49:03 -0700264 char *workbuffer = kzalloc(SPLPAR_MAXLENGTH, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 if (!workbuffer) {
266 printk(KERN_ERR "%s %s kmalloc failure at line %d \n",
Harvey Harrisone48b1b42008-03-29 08:21:07 +1100267 __FILE__, __func__, __LINE__);
David Gibsond3d21762005-11-10 15:26:20 +1100268 kfree(local_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 return;
270 }
271#ifdef LPARCFG_DEBUG
272 printk(KERN_INFO "success calling get-system-parameter \n");
273#endif
Will Schmidt34422fe2006-03-31 09:07:48 -0600274 splpar_strlen = local_buffer[0] * 256 + local_buffer[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 local_buffer += 2; /* step over strlen value */
276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 w_idx = 0;
278 idx = 0;
279 while ((*local_buffer) && (idx < splpar_strlen)) {
280 workbuffer[w_idx++] = local_buffer[idx++];
281 if ((local_buffer[idx] == ',')
282 || (local_buffer[idx] == '\0')) {
283 workbuffer[w_idx] = '\0';
284 if (w_idx) {
285 /* avoid the empty string */
286 seq_printf(m, "%s\n", workbuffer);
287 }
288 memset(workbuffer, 0, SPLPAR_MAXLENGTH);
289 idx++; /* skip the comma */
290 w_idx = 0;
291 } else if (local_buffer[idx] == '=') {
292 /* code here to replace workbuffer contents
293 with different keyword strings */
294 if (0 == strcmp(workbuffer, "MaxEntCap")) {
295 strcpy(workbuffer,
296 "partition_max_entitled_capacity");
297 w_idx = strlen(workbuffer);
298 }
299 if (0 == strcmp(workbuffer, "MaxPlatProcs")) {
300 strcpy(workbuffer,
301 "system_potential_processors");
302 w_idx = strlen(workbuffer);
303 }
304 }
305 }
306 kfree(workbuffer);
307 local_buffer -= 2; /* back up over strlen value */
308 }
309 kfree(local_buffer);
310}
311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312/* Return the number of processors in the system.
313 * This function reads through the device tree and counts
314 * the virtual processors, this does not include threads.
315 */
316static int lparcfg_count_active_processors(void)
317{
318 struct device_node *cpus_dn = NULL;
319 int count = 0;
320
321 while ((cpus_dn = of_find_node_by_type(cpus_dn, "cpu"))) {
322#ifdef LPARCFG_DEBUG
323 printk(KERN_ERR "cpus_dn %p \n", cpus_dn);
324#endif
325 count++;
326 }
327 return count;
328}
329
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000330static int pseries_lparcfg_data(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331{
332 int partition_potential_processors;
333 int partition_active_processors;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 struct device_node *rtas_node;
Jeremy Kerra7f67bd2006-07-12 15:35:54 +1000335 const int *lrdrp = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Stephen Rothwell8c8dc322007-04-24 13:50:55 +1000337 rtas_node = of_find_node_by_path("/rtas");
Olof Johansson2b9a32e2006-02-15 21:40:44 -0600338 if (rtas_node)
Stephen Rothwelle2eb6392007-04-03 22:26:41 +1000339 lrdrp = of_get_property(rtas_node, "ibm,lrdr-capacity", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
341 if (lrdrp == NULL) {
Paul Mackerras271c3f32005-11-11 23:04:40 +1100342 partition_potential_processors = vdso_data->processorCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 } else {
344 partition_potential_processors = *(lrdrp + 4);
345 }
Stephen Rothwell8c8dc322007-04-24 13:50:55 +1000346 of_node_put(rtas_node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
348 partition_active_processors = lparcfg_count_active_processors();
349
Stephen Rothwell1ababe12005-08-03 14:35:25 +1000350 if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 /* this call handles the ibm,get-system-parameter contents */
352 parse_system_parameter_string(m);
Nathan Fotenot11529392008-07-24 04:25:16 +1000353 parse_ppp_data(m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Nathan Fotenot11529392008-07-24 04:25:16 +1000355 seq_printf(m, "purr=%ld\n", get_purr());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 } else { /* non SPLPAR case */
357
358 seq_printf(m, "system_active_processors=%d\n",
359 partition_potential_processors);
360
361 seq_printf(m, "system_potential_processors=%d\n",
362 partition_potential_processors);
363
364 seq_printf(m, "partition_max_entitled_capacity=%d\n",
365 partition_potential_processors * 100);
366
367 seq_printf(m, "partition_entitled_capacity=%d\n",
368 partition_active_processors * 100);
369 }
370
371 seq_printf(m, "partition_active_processors=%d\n",
372 partition_active_processors);
373
374 seq_printf(m, "partition_potential_processors=%d\n",
375 partition_potential_processors);
376
David Gibson3356bb92006-01-13 10:26:42 +1100377 seq_printf(m, "shared_processor_mode=%d\n", lppaca[0].shared_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
379 return 0;
380}
381
Nathan Fotenot11529392008-07-24 04:25:16 +1000382static ssize_t update_ppp(u64 *entitlement, u8 *weight)
383{
384 unsigned long current_entitled;
385 unsigned long dummy;
386 unsigned long resource;
387 u8 current_weight, new_weight;
388 u64 new_entitled;
389 ssize_t retval;
390
391 /* Get our current parameters */
392 retval = h_get_ppp(&current_entitled, &dummy, &dummy, &resource);
393 if (retval)
394 return retval;
395
396 current_weight = (resource >> 5 * 8) & 0xFF;
397
398 if (entitlement) {
399 new_weight = current_weight;
400 new_entitled = *entitlement;
401 } else if (weight) {
402 new_weight = *weight;
403 new_entitled = current_entitled;
404 } else
405 return -EINVAL;
406
407 pr_debug("%s: current_entitled = %lu, current_weight = %u\n",
408 __FUNCTION__, current_entitled, current_weight);
409
410 pr_debug("%s: new_entitled = %lu, new_weight = %u\n",
411 __FUNCTION__, new_entitled, new_weight);
412
413 retval = plpar_hcall_norets(H_SET_PPP, new_entitled, new_weight);
414 return retval;
415}
416
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417/*
418 * Interface for changing system parameters (variable capacity weight
419 * and entitled capacity). Format of input is "param_name=value";
420 * anything after value is ignored. Valid parameters at this time are
421 * "partition_entitled_capacity" and "capacity_weight". We use
422 * H_SET_PPP to alter parameters.
423 *
424 * This function should be invoked only on systems with
425 * FW_FEATURE_SPLPAR.
426 */
427static ssize_t lparcfg_write(struct file *file, const char __user * buf,
428 size_t count, loff_t * off)
429{
430 char *kbuf;
431 char *tmp;
432 u64 new_entitled, *new_entitled_ptr = &new_entitled;
433 u8 new_weight, *new_weight_ptr = &new_weight;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 ssize_t retval = -ENOMEM;
435
Stephen Rothwell0524aad2007-02-05 16:14:05 -0800436 if (!firmware_has_feature(FW_FEATURE_SPLPAR) ||
437 firmware_has_feature(FW_FEATURE_ISERIES))
438 return -EINVAL;
439
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 kbuf = kmalloc(count, GFP_KERNEL);
441 if (!kbuf)
442 goto out;
443
444 retval = -EFAULT;
445 if (copy_from_user(kbuf, buf, count))
446 goto out;
447
448 retval = -EINVAL;
449 kbuf[count - 1] = '\0';
450 tmp = strchr(kbuf, '=');
451 if (!tmp)
452 goto out;
453
454 *tmp++ = '\0';
455
456 if (!strcmp(kbuf, "partition_entitled_capacity")) {
457 char *endp;
458 *new_entitled_ptr = (u64) simple_strtoul(tmp, &endp, 10);
459 if (endp == tmp)
460 goto out;
Nathan Fotenot11529392008-07-24 04:25:16 +1000461
462 retval = update_ppp(new_entitled_ptr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 } else if (!strcmp(kbuf, "capacity_weight")) {
464 char *endp;
465 *new_weight_ptr = (u8) simple_strtoul(tmp, &endp, 10);
466 if (endp == tmp)
467 goto out;
Nathan Fotenot11529392008-07-24 04:25:16 +1000468
469 retval = update_ppp(NULL, new_weight_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 } else
471 goto out;
472
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200473 if (retval == H_SUCCESS || retval == H_CONSTRAINED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 retval = count;
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200475 } else if (retval == H_BUSY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 retval = -EBUSY;
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200477 } else if (retval == H_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 retval = -EIO;
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200479 } else if (retval == H_PARAMETER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 retval = -EINVAL;
481 } else {
482 printk(KERN_WARNING "%s: received unknown hv return code %ld",
Harvey Harrisone48b1b42008-03-29 08:21:07 +1100483 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 retval = -EIO;
485 }
486
Anton Blanchard8acb8882005-11-11 13:53:11 +1100487out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 kfree(kbuf);
489 return retval;
490}
491
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000492#else /* CONFIG_PPC_PSERIES */
493
494static int pseries_lparcfg_data(struct seq_file *m, void *v)
495{
496 return 0;
497}
498
499static ssize_t lparcfg_write(struct file *file, const char __user * buf,
500 size_t count, loff_t * off)
501{
Stephen Rothwell0524aad2007-02-05 16:14:05 -0800502 return -EINVAL;
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000503}
504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505#endif /* CONFIG_PPC_PSERIES */
506
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000507static int lparcfg_data(struct seq_file *m, void *v)
508{
509 struct device_node *rootdn;
510 const char *model = "";
511 const char *system_id = "";
512 const char *tmp;
Jeremy Kerra7f67bd2006-07-12 15:35:54 +1000513 const unsigned int *lp_index_ptr;
514 unsigned int lp_index = 0;
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000515
516 seq_printf(m, "%s %s \n", MODULE_NAME, MODULE_VERS);
517
Stephen Rothwell8c8dc322007-04-24 13:50:55 +1000518 rootdn = of_find_node_by_path("/");
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000519 if (rootdn) {
Stephen Rothwelle2eb6392007-04-03 22:26:41 +1000520 tmp = of_get_property(rootdn, "model", NULL);
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000521 if (tmp) {
522 model = tmp;
523 /* Skip "IBM," - see platforms/iseries/dt.c */
524 if (firmware_has_feature(FW_FEATURE_ISERIES))
525 model += 4;
526 }
Stephen Rothwelle2eb6392007-04-03 22:26:41 +1000527 tmp = of_get_property(rootdn, "system-id", NULL);
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000528 if (tmp) {
529 system_id = tmp;
530 /* Skip "IBM," - see platforms/iseries/dt.c */
531 if (firmware_has_feature(FW_FEATURE_ISERIES))
532 system_id += 4;
533 }
Stephen Rothwelle2eb6392007-04-03 22:26:41 +1000534 lp_index_ptr = of_get_property(rootdn, "ibm,partition-no",
535 NULL);
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000536 if (lp_index_ptr)
537 lp_index = *lp_index_ptr;
Stephen Rothwell8c8dc322007-04-24 13:50:55 +1000538 of_node_put(rootdn);
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000539 }
540 seq_printf(m, "serial_number=%s\n", system_id);
541 seq_printf(m, "system_type=%s\n", model);
542 seq_printf(m, "partition_id=%d\n", (int)lp_index);
543
544 if (firmware_has_feature(FW_FEATURE_ISERIES))
545 return iseries_lparcfg_data(m, v);
546 return pseries_lparcfg_data(m, v);
547}
548
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549static int lparcfg_open(struct inode *inode, struct file *file)
550{
551 return single_open(file, lparcfg_data, NULL);
552}
553
Michael Ellerman1c21a292008-05-08 14:27:19 +1000554static const struct file_operations lparcfg_fops = {
Anton Blanchard8acb8882005-11-11 13:53:11 +1100555 .owner = THIS_MODULE,
556 .read = seq_read,
Stephen Rothwell0524aad2007-02-05 16:14:05 -0800557 .write = lparcfg_write,
Anton Blanchard8acb8882005-11-11 13:53:11 +1100558 .open = lparcfg_open,
559 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560};
561
Michael Ellerman1c21a292008-05-08 14:27:19 +1000562static int __init lparcfg_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563{
564 struct proc_dir_entry *ent;
Wim Coekaerts71839262005-09-05 20:22:47 -0700565 mode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
567 /* Allow writing if we have FW_FEATURE_SPLPAR */
Stephen Rothwell16e9f992006-06-29 15:07:42 +1000568 if (firmware_has_feature(FW_FEATURE_SPLPAR) &&
Stephen Rothwell0524aad2007-02-05 16:14:05 -0800569 !firmware_has_feature(FW_FEATURE_ISERIES))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 mode |= S_IWUSR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Denis V. Lunev66747132008-04-29 01:02:26 -0700572 ent = proc_create("ppc64/lparcfg", mode, NULL, &lparcfg_fops);
573 if (!ent) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 printk(KERN_ERR "Failed to create ppc64/lparcfg\n");
575 return -EIO;
576 }
577
578 proc_ppc64_lparcfg = ent;
579 return 0;
580}
581
Michael Ellerman1c21a292008-05-08 14:27:19 +1000582static void __exit lparcfg_cleanup(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
Nathan Lynch0d9dc4b2007-12-05 03:03:49 +1100584 if (proc_ppc64_lparcfg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 remove_proc_entry("lparcfg", proc_ppc64_lparcfg->parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586}
587
588module_init(lparcfg_init);
589module_exit(lparcfg_cleanup);
590MODULE_DESCRIPTION("Interface for LPAR configuration data");
591MODULE_AUTHOR("Dave Engebretsen");
592MODULE_LICENSE("GPL");